diff options
Diffstat (limited to 'src/fs')
104 files changed, 0 insertions, 44881 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 @@ | |||
1 | gnunet-unindex | ||
2 | gnunet-auto-share | ||
3 | gnunet-daemon-fsprofiler | ||
4 | gnunet-directory | ||
5 | gnunet-download | ||
6 | gnunet-fs | ||
7 | gnunet-fs-profiler | ||
8 | gnunet-helper-fs-publish | ||
9 | gnunet-publish | ||
10 | gnunet-search | ||
11 | gnunet-service-fs | ||
12 | test_fs_directory | ||
13 | test_fs_download | ||
14 | test_fs_download_cadet | ||
15 | test_fs_download_indexed | ||
16 | test_fs_download_persistence | ||
17 | test_fs_file_information | ||
18 | test_fs_getopt | ||
19 | test_fs_list_indexed | ||
20 | test_fs_namespace | ||
21 | test_fs_namespace_list_updateable | ||
22 | test_fs_publish | ||
23 | test_fs_publish_persistence | ||
24 | test_fs_search | ||
25 | test_fs_search_persistence | ||
26 | test_fs_search_probes | ||
27 | test_fs_search_with_and | ||
28 | test_fs_start_stop | ||
29 | test_fs_test_lib | ||
30 | test_fs_unindex | ||
31 | test_fs_unindex_persistence | ||
32 | test_fs_uri | ||
33 | test_gnunet_fs_idx.py | ||
34 | test_gnunet_fs_psd.py | ||
35 | test_gnunet_fs_rec.py | ||
36 | test_gnunet_service_fs_migration | ||
37 | test_gnunet_service_fs_p2p | ||
38 | test_gnunet_service_fs_p2p_cadet | ||
39 | test_plugin_block_fs | ||
40 | perf_gnunet_service_fs_p2p | ||
41 | perf_gnunet_service_fs_p2p_index | ||
42 | perf_gnunet_service_fs_p2p_respect | ||
43 | rdir.gnd | ||
diff --git a/src/fs/Makefile.am b/src/fs/Makefile.am deleted file mode 100644 index 38d75c0dc..000000000 --- a/src/fs/Makefile.am +++ /dev/null | |||
@@ -1,531 +0,0 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | XLIB = -lgcov | ||
7 | endif | ||
8 | |||
9 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
10 | |||
11 | libexecdir= $(pkglibdir)/libexec/ | ||
12 | |||
13 | pkgcfg_DATA = \ | ||
14 | fs.conf | ||
15 | |||
16 | plugindir = $(libdir)/gnunet | ||
17 | |||
18 | |||
19 | lib_LTLIBRARIES = libgnunetfs.la | ||
20 | |||
21 | plugin_LTLIBRARIES = \ | ||
22 | libgnunet_plugin_block_fs.la | ||
23 | |||
24 | libgnunetfs_la_SOURCES = \ | ||
25 | fs_api.c fs_api.h fs.h \ | ||
26 | fs_directory.c \ | ||
27 | fs_dirmetascan.c \ | ||
28 | fs_download.c \ | ||
29 | fs_file_information.c \ | ||
30 | fs_getopt.c \ | ||
31 | fs_list_indexed.c \ | ||
32 | fs_publish.c \ | ||
33 | fs_publish_ksk.c \ | ||
34 | fs_publish_ublock.c fs_publish_ublock.h \ | ||
35 | fs_misc.c \ | ||
36 | fs_namespace.c \ | ||
37 | fs_search.c \ | ||
38 | fs_sharetree.c \ | ||
39 | fs_tree.c fs_tree.h \ | ||
40 | fs_unindex.c \ | ||
41 | fs_uri.c \ | ||
42 | meta_data.c | ||
43 | |||
44 | libgnunetfs_la_LIBADD = \ | ||
45 | $(top_builddir)/src/service/datastore/libgnunetdatastore.la \ | ||
46 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
47 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
48 | $(GN_LIBINTL) $(XLIB) $(LIBGCRYPT_LIBS) -lunistring | ||
49 | |||
50 | if HAVE_LIBEXTRACTOR | ||
51 | libgnunetfs_la_LIBADD += \ | ||
52 | -lextractor | ||
53 | endif | ||
54 | |||
55 | libgnunetfs_la_LDFLAGS = \ | ||
56 | $(GN_LIB_LDFLAGS) \ | ||
57 | -version-info 3:1:1 | ||
58 | |||
59 | |||
60 | libexec_PROGRAMS = \ | ||
61 | gnunet-helper-fs-publish \ | ||
62 | gnunet-service-fs | ||
63 | |||
64 | noinst_PROGRAMS = \ | ||
65 | gnunet-daemon-fsprofiler | ||
66 | |||
67 | bin_PROGRAMS = \ | ||
68 | gnunet-auto-share \ | ||
69 | gnunet-directory \ | ||
70 | gnunet-download \ | ||
71 | gnunet-publish \ | ||
72 | gnunet-search \ | ||
73 | gnunet-fs \ | ||
74 | gnunet-unindex | ||
75 | |||
76 | gnunet_directory_SOURCES = \ | ||
77 | gnunet-directory.c | ||
78 | gnunet_directory_LDADD = \ | ||
79 | libgnunetfs.la \ | ||
80 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
81 | $(GN_LIBINTL) | ||
82 | |||
83 | if HAVE_LIBEXTRACTOR | ||
84 | gnunet_directory_LDADD += \ | ||
85 | -lextractor | ||
86 | endif | ||
87 | |||
88 | gnunet_fs_SOURCES = \ | ||
89 | gnunet-fs.c | ||
90 | gnunet_fs_LDADD = \ | ||
91 | libgnunetfs.la \ | ||
92 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
93 | $(GN_LIBINTL) | ||
94 | |||
95 | if HAVE_LIBEXTRACTOR | ||
96 | gnunet_fs_LDADD += \ | ||
97 | -lextractor | ||
98 | endif | ||
99 | |||
100 | gnunet_download_SOURCES = \ | ||
101 | gnunet-download.c | ||
102 | gnunet_download_LDADD = \ | ||
103 | libgnunetfs.la \ | ||
104 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
105 | $(GN_LIBINTL) | ||
106 | |||
107 | gnunet_publish_SOURCES = \ | ||
108 | gnunet-publish.c | ||
109 | gnunet_publish_LDADD = \ | ||
110 | $(top_builddir)/src/service/identity/libgnunetidentity.la \ | ||
111 | libgnunetfs.la \ | ||
112 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
113 | $(GN_LIBINTL) | ||
114 | |||
115 | if HAVE_LIBEXTRACTOR | ||
116 | gnunet_publish_LDADD += \ | ||
117 | -lextractor | ||
118 | endif | ||
119 | |||
120 | gnunet_auto_share_SOURCES = \ | ||
121 | gnunet-auto-share.c | ||
122 | gnunet_auto_share_LDADD = \ | ||
123 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
124 | $(GN_LIBINTL) | ||
125 | |||
126 | if HAVE_LIBEXTRACTOR | ||
127 | gnunet_auto_share_LDADD += \ | ||
128 | -lextractor | ||
129 | endif | ||
130 | |||
131 | gnunet_helper_fs_publish_SOURCES = \ | ||
132 | gnunet-helper-fs-publish.c | ||
133 | gnunet_helper_fs_publish_LDADD = \ | ||
134 | libgnunetfs.la \ | ||
135 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
136 | $(GN_LIBINTL) | ||
137 | |||
138 | if HAVE_LIBEXTRACTOR | ||
139 | gnunet_helper_fs_publish_LDADD += \ | ||
140 | -lextractor | ||
141 | endif | ||
142 | |||
143 | gnunet_search_SOURCES = \ | ||
144 | gnunet-search.c | ||
145 | gnunet_search_LDADD = \ | ||
146 | libgnunetfs.la \ | ||
147 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
148 | $(GN_LIBINTL) | ||
149 | |||
150 | if HAVE_LIBEXTRACTOR | ||
151 | gnunet_search_LDADD += \ | ||
152 | -lextractor | ||
153 | endif | ||
154 | |||
155 | |||
156 | gnunet_daemon_fsprofiler_SOURCES = \ | ||
157 | gnunet-daemon-fsprofiler.c | ||
158 | gnunet_daemon_fsprofiler_LDADD = \ | ||
159 | libgnunetfs.la \ | ||
160 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
161 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
162 | $(GN_LIBINTL) | ||
163 | |||
164 | gnunet_service_fs_SOURCES = \ | ||
165 | gnunet-service-fs.c gnunet-service-fs.h \ | ||
166 | gnunet-service-fs_cp.c gnunet-service-fs_cp.h \ | ||
167 | gnunet-service-fs_indexing.c gnunet-service-fs_indexing.h \ | ||
168 | gnunet-service-fs_pe.c gnunet-service-fs_pe.h \ | ||
169 | gnunet-service-fs_pr.c gnunet-service-fs_pr.h \ | ||
170 | gnunet-service-fs_push.c gnunet-service-fs_push.h \ | ||
171 | gnunet-service-fs_put.c gnunet-service-fs_put.h \ | ||
172 | gnunet-service-fs_cadet_client.c gnunet-service-fs_cadet.h \ | ||
173 | gnunet-service-fs_cadet_server.c | ||
174 | gnunet_service_fs_LDADD = \ | ||
175 | libgnunetfs.la \ | ||
176 | $(top_builddir)/src/service/dht/libgnunetdht.la \ | ||
177 | $(top_builddir)/src/lib/block/libgnunetblock.la \ | ||
178 | $(top_builddir)/src/service/datastore/libgnunetdatastore.la \ | ||
179 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
180 | $(top_builddir)/src/service/cadet/libgnunetcadet.la \ | ||
181 | $(top_builddir)/src/service/core/libgnunetcore.la \ | ||
182 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
183 | $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ | ||
184 | $(GN_LIBINTL) -lm | ||
185 | |||
186 | gnunet_unindex_SOURCES = \ | ||
187 | gnunet-unindex.c | ||
188 | gnunet_unindex_LDADD = \ | ||
189 | libgnunetfs.la \ | ||
190 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
191 | $(GN_LIBINTL) | ||
192 | |||
193 | libgnunet_plugin_block_fs_la_SOURCES = \ | ||
194 | plugin_block_fs.c | ||
195 | libgnunet_plugin_block_fs_la_LIBADD = \ | ||
196 | $(top_builddir)/src/lib/block/libgnunetblockgroup.la \ | ||
197 | $(top_builddir)/src/lib/block/libgnunetblock.la \ | ||
198 | libgnunetfs.la \ | ||
199 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
200 | $(LTLIBINTL) | ||
201 | libgnunet_plugin_block_fs_la_LDFLAGS = \ | ||
202 | $(GN_PLUGIN_LDFLAGS) | ||
203 | |||
204 | check_PROGRAMS = \ | ||
205 | test_plugin_block_fs \ | ||
206 | test_fs_directory \ | ||
207 | test_fs_download \ | ||
208 | test_fs_download_cadet \ | ||
209 | test_fs_download_indexed \ | ||
210 | test_fs_download_persistence \ | ||
211 | test_fs_file_information \ | ||
212 | test_fs_getopt \ | ||
213 | test_fs_list_indexed \ | ||
214 | test_fs_namespace \ | ||
215 | test_fs_namespace_list_updateable \ | ||
216 | test_fs_publish \ | ||
217 | test_fs_publish_persistence \ | ||
218 | test_fs_search \ | ||
219 | test_fs_search_with_and \ | ||
220 | test_fs_search_probes \ | ||
221 | test_fs_search_persistence \ | ||
222 | test_fs_start_stop \ | ||
223 | test_fs_test_lib \ | ||
224 | test_fs_unindex \ | ||
225 | test_fs_unindex_persistence \ | ||
226 | test_fs_uri \ | ||
227 | test_fs_meta_data \ | ||
228 | test_gnunet_service_fs_migration \ | ||
229 | $(FS_BENCHMARKS) | ||
230 | |||
231 | test_plugin_block_fs_SOURCES = \ | ||
232 | test_plugin_block_fs.c | ||
233 | test_plugin_block_fs_LDADD = \ | ||
234 | $(top_builddir)/src/lib/block/libgnunetblock.la \ | ||
235 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
236 | |||
237 | if HAVE_PYTHON | ||
238 | check_SCRIPTS = \ | ||
239 | test_gnunet_fs_rec.py \ | ||
240 | test_gnunet_fs_idx.py | ||
241 | |||
242 | if HAVE_LIBEXTRACTOR | ||
243 | check_SCRIPTS += \ | ||
244 | test_gnunet_fs_psd.py | ||
245 | endif | ||
246 | endif | ||
247 | |||
248 | |||
249 | if ENABLE_TEST_RUN | ||
250 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
251 | TESTS = test_fs_directory \ | ||
252 | test_fs_file_information \ | ||
253 | test_fs_namespace \ | ||
254 | test_fs_namespace_list_updateable \ | ||
255 | test_fs_search \ | ||
256 | test_fs_search_with_and \ | ||
257 | test_fs_search_probes \ | ||
258 | test_fs_search_persistence \ | ||
259 | test_fs_start_stop \ | ||
260 | test_fs_uri \ | ||
261 | test_fs_meta_data | ||
262 | # $(check_SCRIPTS) | ||
263 | endif | ||
264 | |||
265 | |||
266 | test_fs_directory_SOURCES = \ | ||
267 | test_fs_directory.c | ||
268 | test_fs_directory_LDADD = \ | ||
269 | libgnunetfs.la \ | ||
270 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
271 | |||
272 | if HAVE_LIBEXTRACTOR | ||
273 | test_fs_directory_LDADD += \ | ||
274 | -lextractor | ||
275 | endif | ||
276 | |||
277 | |||
278 | test_fs_download_SOURCES = \ | ||
279 | test_fs_download.c | ||
280 | test_fs_download_LDADD = \ | ||
281 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
282 | libgnunetfs.la \ | ||
283 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
284 | |||
285 | test_fs_download_indexed_SOURCES = \ | ||
286 | test_fs_download.c | ||
287 | test_fs_download_indexed_LDADD = \ | ||
288 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
289 | libgnunetfs.la \ | ||
290 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
291 | |||
292 | test_fs_download_cadet_SOURCES = \ | ||
293 | test_fs_download.c | ||
294 | test_fs_download_cadet_LDADD = \ | ||
295 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
296 | libgnunetfs.la \ | ||
297 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
298 | |||
299 | test_fs_download_persistence_SOURCES = \ | ||
300 | test_fs_download_persistence.c | ||
301 | test_fs_download_persistence_LDADD = \ | ||
302 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
303 | libgnunetfs.la \ | ||
304 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
305 | |||
306 | test_fs_file_information_SOURCES = \ | ||
307 | test_fs_file_information.c | ||
308 | test_fs_file_information_LDADD = \ | ||
309 | libgnunetfs.la \ | ||
310 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
311 | |||
312 | if HAVE_LIBEXTRACTOR | ||
313 | test_fs_file_information_LDADD += \ | ||
314 | -lextractor | ||
315 | endif | ||
316 | |||
317 | |||
318 | test_fs_getopt_SOURCES = \ | ||
319 | test_fs_getopt.c | ||
320 | test_fs_getopt_LDADD = \ | ||
321 | libgnunetfs.la \ | ||
322 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
323 | |||
324 | test_fs_list_indexed_SOURCES = \ | ||
325 | test_fs_list_indexed.c | ||
326 | test_fs_list_indexed_LDADD = \ | ||
327 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
328 | libgnunetfs.la \ | ||
329 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
330 | |||
331 | test_fs_namespace_SOURCES = \ | ||
332 | test_fs_namespace.c | ||
333 | test_fs_namespace_LDADD = \ | ||
334 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
335 | libgnunetfs.la \ | ||
336 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
337 | |||
338 | test_fs_namespace_list_updateable_SOURCES = \ | ||
339 | test_fs_namespace_list_updateable.c | ||
340 | test_fs_namespace_list_updateable_LDADD = \ | ||
341 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
342 | libgnunetfs.la \ | ||
343 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
344 | |||
345 | test_fs_publish_SOURCES = \ | ||
346 | test_fs_publish.c | ||
347 | test_fs_publish_LDADD = \ | ||
348 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
349 | libgnunetfs.la \ | ||
350 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
351 | |||
352 | test_fs_publish_persistence_SOURCES = \ | ||
353 | test_fs_publish_persistence.c | ||
354 | test_fs_publish_persistence_LDADD = \ | ||
355 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
356 | libgnunetfs.la \ | ||
357 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
358 | |||
359 | test_fs_search_SOURCES = \ | ||
360 | test_fs_search.c | ||
361 | test_fs_search_LDADD = \ | ||
362 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
363 | libgnunetfs.la \ | ||
364 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
365 | |||
366 | test_fs_search_with_and_SOURCES = \ | ||
367 | test_fs_search_with_and.c | ||
368 | test_fs_search_with_and_LDADD = \ | ||
369 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
370 | libgnunetfs.la \ | ||
371 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
372 | |||
373 | test_fs_search_probes_SOURCES = \ | ||
374 | test_fs_search_probes.c | ||
375 | test_fs_search_probes_LDADD = \ | ||
376 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
377 | libgnunetfs.la \ | ||
378 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
379 | |||
380 | test_fs_search_persistence_SOURCES = \ | ||
381 | test_fs_search_persistence.c | ||
382 | test_fs_search_persistence_LDADD = \ | ||
383 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
384 | libgnunetfs.la \ | ||
385 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
386 | |||
387 | test_fs_start_stop_SOURCES = \ | ||
388 | test_fs_start_stop.c | ||
389 | test_fs_start_stop_LDADD = \ | ||
390 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
391 | libgnunetfs.la \ | ||
392 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
393 | |||
394 | test_fs_unindex_SOURCES = \ | ||
395 | test_fs_unindex.c | ||
396 | test_fs_unindex_LDADD = \ | ||
397 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
398 | libgnunetfs.la \ | ||
399 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
400 | |||
401 | test_fs_unindex_persistence_SOURCES = \ | ||
402 | test_fs_unindex_persistence.c | ||
403 | test_fs_unindex_persistence_LDADD = \ | ||
404 | $(top_builddir)/src/service/testing/libgnunettesting.la \ | ||
405 | libgnunetfs.la \ | ||
406 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
407 | |||
408 | test_fs_meta_data_SOURCES = \ | ||
409 | test_fs_meta_data.c | ||
410 | test_fs_meta_data_LDADD = \ | ||
411 | libgnunetfs.la \ | ||
412 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
413 | |||
414 | |||
415 | test_fs_uri_SOURCES = \ | ||
416 | test_fs_uri.c | ||
417 | test_fs_uri_LDADD = \ | ||
418 | libgnunetfs.la \ | ||
419 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
420 | |||
421 | # TNG | ||
422 | #test_fs_test_lib_SOURCES = \ | ||
423 | # test_fs_test_lib.c | ||
424 | #test_fs_test_lib_LDADD = \ | ||
425 | # libgnunetfstest.a \ | ||
426 | # $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
427 | # libgnunetfs.la \ | ||
428 | # $(top_builddir)/src/lib/util/libgnunetutil.la | ||
429 | |||
430 | #test_gnunet_service_fs_p2p_SOURCES = \ | ||
431 | # test_gnunet_service_fs_p2p.c | ||
432 | #test_gnunet_service_fs_p2p_LDADD = \ | ||
433 | # libgnunetfstest.a \ | ||
434 | # $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
435 | # libgnunetfs.la \ | ||
436 | # $(top_builddir)/src/lib/util/libgnunetutil.la | ||
437 | # | ||
438 | #test_gnunet_service_fs_p2p_cadet_SOURCES = \ | ||
439 | # test_gnunet_service_fs_p2p.c | ||
440 | #test_gnunet_service_fs_p2p_cadet_LDADD = \ | ||
441 | # libgnunetfstest.a \ | ||
442 | # $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
443 | # libgnunetfs.la \ | ||
444 | # $(top_builddir)/src/lib/util/libgnunetutil.la | ||
445 | # | ||
446 | #test_gnunet_service_fs_migration_SOURCES = \ | ||
447 | # test_gnunet_service_fs_migration.c | ||
448 | #test_gnunet_service_fs_migration_LDADD = \ | ||
449 | # libgnunetfstest.a \ | ||
450 | # $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
451 | # libgnunetfs.la \ | ||
452 | # $(top_builddir)/src/lib/util/libgnunetutil.la | ||
453 | # | ||
454 | #perf_gnunet_service_fs_p2p_SOURCES = \ | ||
455 | # perf_gnunet_service_fs_p2p.c | ||
456 | #perf_gnunet_service_fs_p2p_LDADD = \ | ||
457 | # libgnunetfstest.a \ | ||
458 | # $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
459 | # $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
460 | # libgnunetfs.la \ | ||
461 | # $(top_builddir)/src/lib/util/libgnunetutil.la | ||
462 | # | ||
463 | #perf_gnunet_service_fs_p2p_index_SOURCES = \ | ||
464 | # perf_gnunet_service_fs_p2p.c | ||
465 | #perf_gnunet_service_fs_p2p_index_LDADD = \ | ||
466 | # libgnunetfstest.a \ | ||
467 | # $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
468 | # $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
469 | # libgnunetfs.la \ | ||
470 | # $(top_builddir)/src/lib/util/libgnunetutil.la | ||
471 | # | ||
472 | #perf_gnunet_service_fs_p2p_dht_SOURCES = \ | ||
473 | # perf_gnunet_service_fs_p2p.c | ||
474 | #perf_gnunet_service_fs_p2p_dht_LDADD = \ | ||
475 | # libgnunetfstest.a \ | ||
476 | # $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
477 | # $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
478 | # libgnunetfs.la \ | ||
479 | # $(top_builddir)/src/lib/util/libgnunetutil.la | ||
480 | # | ||
481 | #perf_gnunet_service_fs_p2p_respect_SOURCES = \ | ||
482 | # perf_gnunet_service_fs_p2p_respect.c | ||
483 | #perf_gnunet_service_fs_p2p_respect_LDADD = \ | ||
484 | # libgnunetfstest.a \ | ||
485 | # $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
486 | # $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
487 | # libgnunetfs.la \ | ||
488 | # $(top_builddir)/src/lib/util/libgnunetutil.la | ||
489 | |||
490 | test_gnunet_fs_psd.py: test_gnunet_fs_psd.py.in Makefile | ||
491 | $(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 | ||
492 | chmod +x test_gnunet_fs_psd.py | ||
493 | |||
494 | test_gnunet_fs_rec.py: test_gnunet_fs_rec.py.in Makefile | ||
495 | $(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 | ||
496 | chmod +x test_gnunet_fs_rec.py | ||
497 | |||
498 | test_gnunet_fs_ns.py: test_gnunet_fs_ns.py.in Makefile | ||
499 | $(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 | ||
500 | chmod +x test_gnunet_fs_ns.py | ||
501 | |||
502 | test_gnunet_fs_idx.py: test_gnunet_fs_idx.py.in Makefile | ||
503 | $(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 | ||
504 | chmod +x test_gnunet_fs_idx.py | ||
505 | |||
506 | |||
507 | EXTRA_DIST = \ | ||
508 | fs_test_lib_data.conf \ | ||
509 | perf_gnunet_service_fs_p2p.conf \ | ||
510 | test_fs_data.conf \ | ||
511 | test_fs_defaults.conf \ | ||
512 | test_fs_download_data.conf \ | ||
513 | test_fs_download_indexed.conf \ | ||
514 | test_fs_file_information_data.conf \ | ||
515 | test_fs_list_indexed_data.conf \ | ||
516 | test_fs_namespace_data.conf \ | ||
517 | test_fs_publish_data.conf \ | ||
518 | test_fs_search_data.conf \ | ||
519 | test_fs_unindex_data.conf \ | ||
520 | test_gnunet_fs_idx_data.conf \ | ||
521 | test_gnunet_fs_psd_data.conf \ | ||
522 | test_gnunet_fs_rec_data.conf \ | ||
523 | test_gnunet_fs_rec_data.tgz \ | ||
524 | test_gnunet_fs_psd.py.in \ | ||
525 | test_gnunet_fs_rec.py.in \ | ||
526 | test_gnunet_fs_idx.py.in \ | ||
527 | test_gnunet_service_fs_migration_data.conf \ | ||
528 | test_gnunet_service_fs_p2p_cadet.conf \ | ||
529 | test_pseudonym_data.conf | ||
530 | |||
531 | CLEANFILES = $(check_SCRIPTS) | ||
diff --git a/src/fs/fs.conf.in b/src/fs/fs.conf.in deleted file mode 100644 index be02619bf..000000000 --- a/src/fs/fs.conf.in +++ /dev/null | |||
@@ -1,63 +0,0 @@ | |||
1 | [fs] | ||
2 | RUN_PER_USER = YES | ||
3 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
4 | IMMEDIATE_START = YES | ||
5 | INDEXDB = $GNUNET_DATA_HOME/fs/idxinfo.lst | ||
6 | RESPECT = $GNUNET_DATA_HOME/fs/credit/ | ||
7 | STATE_DIR = $GNUNET_DATA_HOME/fs/persistence/ | ||
8 | UPDATE_DIR = $GNUNET_DATA_HOME/fs/updates/ | ||
9 | @UNIXONLY@ PORT = 2094 | ||
10 | HOSTNAME = localhost | ||
11 | BINARY = gnunet-service-fs | ||
12 | ACCEPT_FROM = 127.0.0.1; | ||
13 | ACCEPT_FROM6 = ::1; | ||
14 | |||
15 | # PREFIX = valgrind | ||
16 | |||
17 | # Do we introduce artificial delays? (may improve anonymity) | ||
18 | DELAY = YES | ||
19 | |||
20 | # Do we cache content from other nodes? (may improve anonymity) | ||
21 | CONTENT_CACHING = YES | ||
22 | |||
23 | # Do we send unsolicited data to other nodes if we have excess bandwidth? | ||
24 | # (may improve anonymity, probably not a good idea if content_caching is NO) | ||
25 | CONTENT_PUSHING = YES | ||
26 | |||
27 | UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-fs.sock | ||
28 | |||
29 | # Do we require users that want to access file-sharing to run this process | ||
30 | # (usually not a good idea) | ||
31 | UNIX_MATCH_UID = NO | ||
32 | |||
33 | # Do we require users that want to access file-sharing to be in the 'gnunet' group? | ||
34 | UNIX_MATCH_GID = YES | ||
35 | |||
36 | # Maximum number of requests this peer tracks (important for | ||
37 | # memory consumption; 2k RAM/request is not unusual) | ||
38 | MAX_PENDING_REQUESTS = 65536 | ||
39 | |||
40 | # How many requests do we have at most waiting in the queue towards | ||
41 | # the datastore? (important for memory consumption) | ||
42 | DATASTORE_QUEUE_SIZE = 32 | ||
43 | |||
44 | # Maximum frequency we're allowed to poll the datastore | ||
45 | # for content for migration (can be used to reduce | ||
46 | # GNUnet's disk-IO rate) | ||
47 | MIN_MIGRATION_DELAY = 100 ms | ||
48 | |||
49 | # For how many neighbouring peers should we allocate hash maps? | ||
50 | EXPECTED_NEIGHBOUR_COUNT = 128 | ||
51 | |||
52 | # Disable anonymous file-sharing (but keep non-anonymous transfers)? | ||
53 | # This option is mostly for testing. | ||
54 | DISABLE_ANON_TRANSFER = NO | ||
55 | |||
56 | # Maximum number of non-anonymous transfers this peer will support | ||
57 | # at the same time. Excessive values mostly have the problem that | ||
58 | # the service might use more memory, so we need to bound this at | ||
59 | # some reasonable level. And if we have a very, very large | ||
60 | # number, we probably won't have enough bandwidth to support them | ||
61 | # well anyway, so better have a moderate cap. | ||
62 | MAX_CADET_CLIENTS = 128 | ||
63 | |||
diff --git a/src/fs/fs.h b/src/fs/fs.h deleted file mode 100644 index c3bae65d2..000000000 --- a/src/fs/fs.h +++ /dev/null | |||
@@ -1,397 +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 | |||
33 | #include "gnunet_fs_service.h" | ||
34 | #include "gnunet_block_lib.h" | ||
35 | #include "block_fs.h" | ||
36 | |||
37 | |||
38 | /** | ||
39 | * Size of the individual blocks used for file-sharing. | ||
40 | */ | ||
41 | #define DBLOCK_SIZE (32 * 1024) | ||
42 | |||
43 | /** | ||
44 | * Blocksize to use when hashing files for indexing (blocksize for IO, | ||
45 | * not for the DBlocks). Larger blocksizes can be more efficient but | ||
46 | * will be more disruptive as far as the scheduler is concerned. | ||
47 | */ | ||
48 | #define HASHING_BLOCKSIZE (1024 * 128) | ||
49 | |||
50 | |||
51 | /** | ||
52 | * @brief content hash key | ||
53 | */ | ||
54 | struct ContentHashKey | ||
55 | { | ||
56 | /** | ||
57 | * Hash of the original content, used for encryption. | ||
58 | */ | ||
59 | struct GNUNET_HashCode key; | ||
60 | |||
61 | /** | ||
62 | * Hash of the encrypted content, used for querying. | ||
63 | */ | ||
64 | struct GNUNET_HashCode query; | ||
65 | }; | ||
66 | |||
67 | |||
68 | GNUNET_NETWORK_STRUCT_BEGIN | ||
69 | |||
70 | |||
71 | /** | ||
72 | * Message sent from a GNUnet (fs) publishing activity to sign | ||
73 | * a LOC URI. | ||
74 | */ | ||
75 | struct RequestLocSignatureMessage | ||
76 | { | ||
77 | /** | ||
78 | * Message type will be #GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGN. | ||
79 | */ | ||
80 | struct GNUNET_MessageHeader header; | ||
81 | |||
82 | /** | ||
83 | * Requested signature purpose. For now, always | ||
84 | * #GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT. | ||
85 | */ | ||
86 | uint32_t purpose GNUNET_PACKED; | ||
87 | |||
88 | /** | ||
89 | * Requested expiration time. | ||
90 | */ | ||
91 | struct GNUNET_TIME_AbsoluteNBO expiration_time; | ||
92 | |||
93 | /** | ||
94 | * Information about the shared file (to be signed). | ||
95 | */ | ||
96 | struct ContentHashKey chk; | ||
97 | |||
98 | /** | ||
99 | * Size of the shared file (to be signed). | ||
100 | */ | ||
101 | uint64_t file_length; | ||
102 | }; | ||
103 | |||
104 | |||
105 | /** | ||
106 | * Message sent from the service with the signed LOC URI. | ||
107 | */ | ||
108 | struct ResponseLocSignatureMessage | ||
109 | { | ||
110 | /** | ||
111 | * Message type will be | ||
112 | * #GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGNATURE. | ||
113 | */ | ||
114 | struct GNUNET_MessageHeader header; | ||
115 | |||
116 | /** | ||
117 | * Purpose of the generated signature. For now, always | ||
118 | * #GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT. | ||
119 | */ | ||
120 | uint32_t purpose GNUNET_PACKED; | ||
121 | |||
122 | /** | ||
123 | * Expiration time that was actually used (rounded!). | ||
124 | */ | ||
125 | struct GNUNET_TIME_AbsoluteNBO expiration_time; | ||
126 | |||
127 | /** | ||
128 | * The requested signature. | ||
129 | */ | ||
130 | struct GNUNET_CRYPTO_EddsaSignature signature; | ||
131 | |||
132 | /** | ||
133 | * Identity of the peer sharing the file. | ||
134 | */ | ||
135 | struct GNUNET_PeerIdentity peer; | ||
136 | }; | ||
137 | |||
138 | |||
139 | /** | ||
140 | * Message sent from a GNUnet (fs) publishing activity to the | ||
141 | * gnunet-fs-service to initiate indexing of a file. The service is | ||
142 | * supposed to check if the specified file is available and has the | ||
143 | * same cryptographic hash. It should then respond with either a | ||
144 | * confirmation or a denial. | ||
145 | * | ||
146 | * On OSes where this works, it is considered acceptable if the | ||
147 | * service only checks that the path, device and inode match (it can | ||
148 | * then be assumed that the hash will also match without actually | ||
149 | * computing it; this is an optimization that should be safe given | ||
150 | * that the client is not our adversary). | ||
151 | */ | ||
152 | struct IndexStartMessage | ||
153 | { | ||
154 | /** | ||
155 | * Message type will be #GNUNET_MESSAGE_TYPE_FS_INDEX_START. | ||
156 | */ | ||
157 | struct GNUNET_MessageHeader header; | ||
158 | |||
159 | /** | ||
160 | * For alignment. | ||
161 | */ | ||
162 | uint32_t reserved GNUNET_PACKED; | ||
163 | |||
164 | /** | ||
165 | * ID of device containing the file, as seen by the client. This | ||
166 | * device ID is obtained using a call like "statvfs" (and converting | ||
167 | * the "f_fsid" field to a 32-bit big-endian number). Use 0 if the | ||
168 | * OS does not support this, in which case the service must do a | ||
169 | * full hash recomputation. | ||
170 | */ | ||
171 | uint64_t device GNUNET_PACKED; | ||
172 | |||
173 | /** | ||
174 | * Inode of the file on the given device, as seen by the client | ||
175 | * ("st_ino" field from "struct stat"). Use 0 if the OS does not | ||
176 | * support this, in which case the service must do a full hash | ||
177 | * recomputation. | ||
178 | */ | ||
179 | uint64_t inode GNUNET_PACKED; | ||
180 | |||
181 | /** | ||
182 | * Hash of the file that we would like to index. | ||
183 | */ | ||
184 | struct GNUNET_HashCode file_id; | ||
185 | |||
186 | /* this is followed by a 0-terminated | ||
187 | * filename of a file with the hash | ||
188 | * "file_id" as seen by the client */ | ||
189 | }; | ||
190 | |||
191 | |||
192 | /** | ||
193 | * Message send by FS service in response to a request | ||
194 | * asking for a list of all indexed files. | ||
195 | */ | ||
196 | struct IndexInfoMessage | ||
197 | { | ||
198 | /** | ||
199 | * Message type will be | ||
200 | * #GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY. | ||
201 | */ | ||
202 | struct GNUNET_MessageHeader header; | ||
203 | |||
204 | /** | ||
205 | * Always zero. | ||
206 | */ | ||
207 | uint32_t reserved GNUNET_PACKED; | ||
208 | |||
209 | /** | ||
210 | * Hash of the indexed file. | ||
211 | */ | ||
212 | struct GNUNET_HashCode file_id; | ||
213 | |||
214 | /* this is followed by a 0-terminated | ||
215 | * filename of a file with the hash | ||
216 | * "file_id" as seen by the client */ | ||
217 | }; | ||
218 | |||
219 | |||
220 | /** | ||
221 | * Message sent from a GNUnet (fs) unindexing activity to the | ||
222 | * gnunet-service-fs to indicate that a file will be unindexed. The | ||
223 | * service is supposed to remove the file from the list of indexed | ||
224 | * files and response with a confirmation message (even if the file | ||
225 | * was already not on the list). | ||
226 | */ | ||
227 | struct UnindexMessage | ||
228 | { | ||
229 | /** | ||
230 | * Message type will be #GNUNET_MESSAGE_TYPE_FS_UNINDEX. | ||
231 | */ | ||
232 | struct GNUNET_MessageHeader header; | ||
233 | |||
234 | /** | ||
235 | * Always zero. | ||
236 | */ | ||
237 | uint32_t reserved GNUNET_PACKED; | ||
238 | |||
239 | /** | ||
240 | * Hash of the file that we will unindex. | ||
241 | */ | ||
242 | struct GNUNET_HashCode file_id; | ||
243 | }; | ||
244 | |||
245 | |||
246 | /** | ||
247 | * No options. | ||
248 | */ | ||
249 | #define SEARCH_MESSAGE_OPTION_NONE 0 | ||
250 | |||
251 | /** | ||
252 | * Only search the local datastore (no network) | ||
253 | */ | ||
254 | #define SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY 1 | ||
255 | |||
256 | /** | ||
257 | * Request is too large to fit in 64k format. The list of | ||
258 | * already-known search results will be continued in another message | ||
259 | * for the same type/query/target and additional already-known results | ||
260 | * following this one). | ||
261 | */ | ||
262 | #define SEARCH_MESSAGE_OPTION_CONTINUED 2 | ||
263 | |||
264 | |||
265 | /** | ||
266 | * Message sent from a GNUnet (fs) search activity to the | ||
267 | * gnunet-service-fs to start a search. | ||
268 | */ | ||
269 | struct SearchMessage | ||
270 | { | ||
271 | /** | ||
272 | * Message type will be #GNUNET_MESSAGE_TYPE_FS_START_SEARCH. | ||
273 | */ | ||
274 | struct GNUNET_MessageHeader header; | ||
275 | |||
276 | /** | ||
277 | * Bitmask with options. Zero for no options, one for | ||
278 | * loopback-only, two for 'to be continued' (with a second search | ||
279 | * message for the same type/query/target and additional | ||
280 | * already-known results following this one). See | ||
281 | * SEARCH_MESSAGE_OPTION_ defines. | ||
282 | * | ||
283 | * Other bits are currently not defined. | ||
284 | */ | ||
285 | uint32_t options GNUNET_PACKED; | ||
286 | |||
287 | /** | ||
288 | * Type of the content that we're looking for. | ||
289 | */ | ||
290 | uint32_t type GNUNET_PACKED; | ||
291 | |||
292 | /** | ||
293 | * Desired anonymity level, big-endian. | ||
294 | */ | ||
295 | uint32_t anonymity_level GNUNET_PACKED; | ||
296 | |||
297 | /** | ||
298 | * If the request is for a DBLOCK or IBLOCK, this is the identity of | ||
299 | * the peer that is known to have a response. Set to all-zeros if | ||
300 | * such a target is not known (note that even if OUR anonymity | ||
301 | * level is >0 we may happen to know the responder's identity; | ||
302 | * nevertheless, we should probably not use it for a DHT-lookup | ||
303 | * or similar blunt actions in order to avoid exposing ourselves). | ||
304 | * <p> | ||
305 | * Otherwise, "target" must be all zeros. | ||
306 | */ | ||
307 | struct GNUNET_PeerIdentity target; | ||
308 | |||
309 | /** | ||
310 | * Hash of the public key for UBLOCKs; Hash of | ||
311 | * the CHK-encoded block for DBLOCKS and IBLOCKS. | ||
312 | */ | ||
313 | struct GNUNET_HashCode query; | ||
314 | |||
315 | /* this is followed by the hash codes of already-known | ||
316 | * results (which should hence be excluded from what | ||
317 | * the service returns); naturally, this only applies | ||
318 | * to queries that can have multiple results (UBLOCKS). | ||
319 | */ | ||
320 | }; | ||
321 | |||
322 | |||
323 | /** | ||
324 | * Response from FS service with a result for a previous FS search. | ||
325 | * Note that queries for DBLOCKS and IBLOCKS that have received a | ||
326 | * single response are considered done. This message is transmitted | ||
327 | * between peers. | ||
328 | */ | ||
329 | struct PutMessage | ||
330 | { | ||
331 | /** | ||
332 | * Message type will be #GNUNET_MESSAGE_TYPE_FS_PUT. | ||
333 | */ | ||
334 | struct GNUNET_MessageHeader header; | ||
335 | |||
336 | /** | ||
337 | * Type of the block (in big endian). Should never be zero. | ||
338 | */ | ||
339 | uint32_t type GNUNET_PACKED; | ||
340 | |||
341 | /** | ||
342 | * When does this result expire? | ||
343 | */ | ||
344 | struct GNUNET_TIME_AbsoluteNBO expiration; | ||
345 | |||
346 | /* this is followed by the actual encrypted content */ | ||
347 | }; | ||
348 | |||
349 | /** | ||
350 | * Response from FS service with a result for a previous FS search. | ||
351 | * Note that queries for DBLOCKS and IBLOCKS that have received a | ||
352 | * single response are considered done. This message is transmitted | ||
353 | * between the service and a client. | ||
354 | */ | ||
355 | struct ClientPutMessage | ||
356 | { | ||
357 | /** | ||
358 | * Message type will be #GNUNET_MESSAGE_TYPE_FS_PUT. | ||
359 | */ | ||
360 | struct GNUNET_MessageHeader header; | ||
361 | |||
362 | /** | ||
363 | * Type of the block (in big endian). Should never be zero. | ||
364 | */ | ||
365 | uint32_t type GNUNET_PACKED; | ||
366 | |||
367 | /** | ||
368 | * When does this result expire? | ||
369 | */ | ||
370 | struct GNUNET_TIME_AbsoluteNBO expiration; | ||
371 | |||
372 | /** | ||
373 | * When was the last time we've tried to download this block? | ||
374 | * (FOREVER if unknown/not relevant) | ||
375 | */ | ||
376 | struct GNUNET_TIME_AbsoluteNBO last_transmission; | ||
377 | |||
378 | /** | ||
379 | * How often did we transmit this query before getting an | ||
380 | * answer (estimate). | ||
381 | */ | ||
382 | uint32_t num_transmissions; | ||
383 | |||
384 | /** | ||
385 | * How much respect did we offer (in total) before getting an | ||
386 | * answer (estimate). | ||
387 | */ | ||
388 | uint32_t respect_offered; | ||
389 | |||
390 | /* this is followed by the actual encrypted content */ | ||
391 | }; | ||
392 | GNUNET_NETWORK_STRUCT_END | ||
393 | |||
394 | |||
395 | #endif | ||
396 | |||
397 | /* end of fs.h */ | ||
diff --git a/src/fs/fs_api.c b/src/fs/fs_api.c deleted file mode 100644 index 627c58004..000000000 --- a/src/fs/fs_api.c +++ /dev/null | |||
@@ -1,3321 +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 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | #include "fs_api.h" | ||
32 | #include "fs_tree.h" | ||
33 | |||
34 | /** | ||
35 | * How many block requests can we have outstanding in parallel at a time by default? | ||
36 | */ | ||
37 | #define DEFAULT_MAX_PARALLEL_REQUESTS (1024 * 10) | ||
38 | |||
39 | /** | ||
40 | * How many downloads can we have outstanding in parallel at a time by default? | ||
41 | */ | ||
42 | #define DEFAULT_MAX_PARALLEL_DOWNLOADS 16 | ||
43 | |||
44 | /** | ||
45 | * Start the given job (send signal, remove from pending queue, update | ||
46 | * counters and state). | ||
47 | * | ||
48 | * @param qe job to start | ||
49 | */ | ||
50 | static void | ||
51 | start_job (struct GNUNET_FS_QueueEntry *qe) | ||
52 | { | ||
53 | qe->active = GNUNET_YES; | ||
54 | qe->start (qe->cls); | ||
55 | qe->start_times++; | ||
56 | qe->h->active_blocks += qe->blocks; | ||
57 | qe->h->active_downloads++; | ||
58 | qe->start_time = GNUNET_TIME_absolute_get (); | ||
59 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
60 | "Starting job %p (%u active)\n", | ||
61 | qe, | ||
62 | qe->h->active_downloads); | ||
63 | GNUNET_CONTAINER_DLL_remove (qe->h->pending_head, qe->h->pending_tail, qe); | ||
64 | GNUNET_CONTAINER_DLL_insert_after (qe->h->running_head, | ||
65 | qe->h->running_tail, | ||
66 | qe->h->running_tail, | ||
67 | qe); | ||
68 | } | ||
69 | |||
70 | |||
71 | /** | ||
72 | * Stop the given job (send signal, remove from active queue, update | ||
73 | * counters and state). | ||
74 | * | ||
75 | * @param qe job to stop | ||
76 | */ | ||
77 | static void | ||
78 | stop_job (struct GNUNET_FS_QueueEntry *qe) | ||
79 | { | ||
80 | qe->active = GNUNET_NO; | ||
81 | qe->stop (qe->cls); | ||
82 | GNUNET_assert (0 < qe->h->active_downloads); | ||
83 | qe->h->active_downloads--; | ||
84 | qe->h->active_blocks -= qe->blocks; | ||
85 | qe->run_time = GNUNET_TIME_relative_add (qe->run_time, | ||
86 | GNUNET_TIME_absolute_get_duration ( | ||
87 | qe->start_time)); | ||
88 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
89 | "Stopping job %p (%u active)\n", | ||
90 | qe, | ||
91 | qe->h->active_downloads); | ||
92 | GNUNET_CONTAINER_DLL_remove (qe->h->running_head, qe->h->running_tail, qe); | ||
93 | GNUNET_CONTAINER_DLL_insert_after (qe->h->pending_head, | ||
94 | qe->h->pending_tail, | ||
95 | qe->h->pending_tail, | ||
96 | qe); | ||
97 | } | ||
98 | |||
99 | |||
100 | /** | ||
101 | * Process the jobs in the job queue, possibly starting some | ||
102 | * and stopping others. | ||
103 | * | ||
104 | * @param cls the `struct GNUNET_FS_Handle *` | ||
105 | */ | ||
106 | static void | ||
107 | process_job_queue (void *cls) | ||
108 | { | ||
109 | struct GNUNET_FS_Handle *h = cls; | ||
110 | struct GNUNET_FS_QueueEntry *qe; | ||
111 | struct GNUNET_FS_QueueEntry *next; | ||
112 | struct GNUNET_TIME_Relative run_time; | ||
113 | struct GNUNET_TIME_Relative restart_at; | ||
114 | struct GNUNET_TIME_Relative rst; | ||
115 | struct GNUNET_TIME_Absolute end_time; | ||
116 | unsigned int num_downloads_waiting; | ||
117 | unsigned int num_downloads_active; | ||
118 | unsigned int num_downloads_expired; | ||
119 | unsigned int num_probes_active; | ||
120 | unsigned int num_probes_waiting; | ||
121 | unsigned int num_probes_expired; | ||
122 | int num_probes_change; | ||
123 | int num_downloads_change; | ||
124 | int block_limit_hit; | ||
125 | |||
126 | h->queue_job = NULL; | ||
127 | /* restart_at will be set to the time when it makes sense to | ||
128 | re-evaluate the job queue (unless, of course, jobs complete | ||
129 | or are added, then we'll be triggered immediately */ | ||
130 | restart_at = GNUNET_TIME_UNIT_FOREVER_REL; | ||
131 | /* first, calculate some basic statistics on pending jobs */ | ||
132 | num_probes_waiting = 0; | ||
133 | num_downloads_waiting = 0; | ||
134 | for (qe = h->pending_head; NULL != qe; qe = qe->next) | ||
135 | { | ||
136 | switch (qe->priority) | ||
137 | { | ||
138 | case GNUNET_FS_QUEUE_PRIORITY_PROBE: | ||
139 | num_probes_waiting++; | ||
140 | break; | ||
141 | |||
142 | case GNUNET_FS_QUEUE_PRIORITY_NORMAL: | ||
143 | num_downloads_waiting++; | ||
144 | break; | ||
145 | |||
146 | default: | ||
147 | GNUNET_break (0); | ||
148 | break; | ||
149 | } | ||
150 | } | ||
151 | /* now, calculate some basic statistics on running jobs */ | ||
152 | num_probes_active = 0; | ||
153 | num_probes_expired = 0; | ||
154 | num_downloads_active = 0; | ||
155 | num_downloads_expired = 0; | ||
156 | next = h->running_head; | ||
157 | while (NULL != (qe = next)) | ||
158 | { | ||
159 | next = qe->next; | ||
160 | switch (qe->priority) | ||
161 | { | ||
162 | case GNUNET_FS_QUEUE_PRIORITY_PROBE: | ||
163 | run_time = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2); | ||
164 | end_time = GNUNET_TIME_absolute_add (qe->start_time, run_time); | ||
165 | rst = GNUNET_TIME_absolute_get_remaining (end_time); | ||
166 | if (0 == rst.rel_value_us) | ||
167 | { | ||
168 | num_probes_expired++; | ||
169 | stop_job (qe); | ||
170 | } | ||
171 | else | ||
172 | { | ||
173 | num_probes_active++; | ||
174 | restart_at = GNUNET_TIME_relative_min (rst, restart_at); | ||
175 | } | ||
176 | break; | ||
177 | |||
178 | case GNUNET_FS_QUEUE_PRIORITY_NORMAL: | ||
179 | run_time = | ||
180 | GNUNET_TIME_relative_saturating_multiply (h->avg_block_latency, | ||
181 | qe->blocks * qe->start_times); | ||
182 | end_time = GNUNET_TIME_absolute_add (qe->start_time, run_time); | ||
183 | rst = GNUNET_TIME_absolute_get_remaining (end_time); | ||
184 | if (0 == rst.rel_value_us) | ||
185 | { | ||
186 | num_downloads_expired++; | ||
187 | stop_job (qe); | ||
188 | } | ||
189 | else | ||
190 | { | ||
191 | num_downloads_active++; | ||
192 | restart_at = GNUNET_TIME_relative_min (rst, restart_at); | ||
193 | } | ||
194 | break; | ||
195 | |||
196 | default: | ||
197 | GNUNET_break (0); | ||
198 | break; | ||
199 | } | ||
200 | } | ||
201 | GNUNET_break (h->active_downloads == | ||
202 | num_downloads_active + num_probes_active); | ||
203 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
204 | "PA: %u, PE: %u, PW: %u; DA: %u, DE: %u, DW: %u\n", | ||
205 | num_probes_active, | ||
206 | num_probes_expired, | ||
207 | num_probes_waiting, | ||
208 | num_downloads_active, | ||
209 | num_downloads_expired, | ||
210 | num_downloads_waiting); | ||
211 | GNUNET_break (h->active_downloads + num_probes_active <= | ||
212 | h->max_parallel_downloads); | ||
213 | /* calculate start/stop decisions */ | ||
214 | if (h->active_downloads + num_downloads_waiting > h->max_parallel_downloads) | ||
215 | { | ||
216 | /* stop as many probes as there are downloads and probes */ | ||
217 | num_probes_change = -GNUNET_MIN (num_probes_active, num_downloads_waiting); | ||
218 | /* start as many downloads as there are free slots, including those | ||
219 | we just opened up */ | ||
220 | num_downloads_change = | ||
221 | h->max_parallel_downloads - h->active_downloads - num_probes_change; | ||
222 | } | ||
223 | else | ||
224 | { | ||
225 | /* start all downloads (we can) */ | ||
226 | num_downloads_change = num_downloads_waiting; | ||
227 | /* also start probes if there is room, but use a lower cap of (mpd/4) + 1 */ | ||
228 | if (1 + h->max_parallel_downloads / 4 >= | ||
229 | (h->active_downloads + num_downloads_change)) | ||
230 | num_probes_change = | ||
231 | GNUNET_MIN (num_probes_waiting, | ||
232 | (1 + h->max_parallel_downloads / 4) | ||
233 | - (h->active_downloads + num_downloads_change)); | ||
234 | else | ||
235 | num_probes_change = 0; | ||
236 | } | ||
237 | GNUNET_break (num_downloads_change <= num_downloads_waiting); | ||
238 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
239 | "Changing %d probes and %d/%u/%u downloads\n", | ||
240 | num_probes_change, | ||
241 | num_downloads_change, | ||
242 | (unsigned int) h->active_downloads, | ||
243 | (unsigned int) h->max_parallel_downloads); | ||
244 | /* actually stop probes */ | ||
245 | next = h->running_head; | ||
246 | while (NULL != (qe = next)) | ||
247 | { | ||
248 | next = qe->next; | ||
249 | if (GNUNET_FS_QUEUE_PRIORITY_PROBE != qe->priority) | ||
250 | continue; | ||
251 | if (num_probes_change < 0) | ||
252 | { | ||
253 | stop_job (qe); | ||
254 | num_probes_change++; | ||
255 | if (0 == num_probes_change) | ||
256 | break; | ||
257 | } | ||
258 | } | ||
259 | GNUNET_break (0 <= num_probes_change); | ||
260 | |||
261 | /* start some more tasks if we now have empty slots */ | ||
262 | block_limit_hit = GNUNET_NO; | ||
263 | next = h->pending_head; | ||
264 | while ((NULL != (qe = next)) && | ||
265 | ((num_probes_change > 0) || (num_downloads_change > 0))) | ||
266 | { | ||
267 | next = qe->next; | ||
268 | switch (qe->priority) | ||
269 | { | ||
270 | case GNUNET_FS_QUEUE_PRIORITY_PROBE: | ||
271 | if (num_probes_change > 0) | ||
272 | { | ||
273 | start_job (qe); | ||
274 | num_probes_change--; | ||
275 | run_time = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2); | ||
276 | restart_at = GNUNET_TIME_relative_min (run_time, restart_at); | ||
277 | } | ||
278 | break; | ||
279 | |||
280 | case GNUNET_FS_QUEUE_PRIORITY_NORMAL: | ||
281 | if ((num_downloads_change > 0) && | ||
282 | ((qe->blocks + h->active_blocks <= h->max_parallel_requests) || | ||
283 | ((qe->blocks > h->max_parallel_requests) && | ||
284 | (0 == h->active_downloads)))) | ||
285 | { | ||
286 | start_job (qe); | ||
287 | num_downloads_change--; | ||
288 | } | ||
289 | else if (num_downloads_change > 0) | ||
290 | block_limit_hit = GNUNET_YES; | ||
291 | break; | ||
292 | |||
293 | default: | ||
294 | GNUNET_break (0); | ||
295 | break; | ||
296 | } | ||
297 | } | ||
298 | GNUNET_break ((0 == num_downloads_change) || (GNUNET_YES == block_limit_hit)); | ||
299 | GNUNET_break (0 == num_probes_change); | ||
300 | |||
301 | GNUNET_log ( | ||
302 | GNUNET_ERROR_TYPE_DEBUG, | ||
303 | "AD: %u, MP: %u; %d probes and %d downloads to start, will run again in %s\n", | ||
304 | h->active_downloads, | ||
305 | h->max_parallel_requests, | ||
306 | num_probes_change, | ||
307 | num_downloads_change, | ||
308 | GNUNET_STRINGS_relative_time_to_string (restart_at, GNUNET_YES)); | ||
309 | |||
310 | /* make sure we run again, callbacks might have | ||
311 | already re-scheduled the job, so cancel such | ||
312 | an operation (if it exists) */ | ||
313 | if (NULL != h->queue_job) | ||
314 | GNUNET_SCHEDULER_cancel (h->queue_job); | ||
315 | h->queue_job = | ||
316 | GNUNET_SCHEDULER_add_delayed (restart_at, &process_job_queue, h); | ||
317 | } | ||
318 | |||
319 | |||
320 | struct GNUNET_FS_QueueEntry * | ||
321 | GNUNET_FS_queue_ (struct GNUNET_FS_Handle *h, | ||
322 | GNUNET_SCHEDULER_TaskCallback start, | ||
323 | GNUNET_SCHEDULER_TaskCallback stop, | ||
324 | void *cls, | ||
325 | unsigned int blocks, | ||
326 | enum GNUNET_FS_QueuePriority priority) | ||
327 | { | ||
328 | struct GNUNET_FS_QueueEntry *qe; | ||
329 | |||
330 | qe = GNUNET_new (struct GNUNET_FS_QueueEntry); | ||
331 | qe->h = h; | ||
332 | qe->start = start; | ||
333 | qe->stop = stop; | ||
334 | qe->cls = cls; | ||
335 | qe->queue_time = GNUNET_TIME_absolute_get (); | ||
336 | qe->blocks = blocks; | ||
337 | qe->priority = priority; | ||
338 | GNUNET_CONTAINER_DLL_insert_after (h->pending_head, | ||
339 | h->pending_tail, | ||
340 | h->pending_tail, | ||
341 | qe); | ||
342 | if (NULL != h->queue_job) | ||
343 | GNUNET_SCHEDULER_cancel (h->queue_job); | ||
344 | h->queue_job = GNUNET_SCHEDULER_add_now (&process_job_queue, h); | ||
345 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Queueing job %p\n", qe); | ||
346 | return qe; | ||
347 | } | ||
348 | |||
349 | |||
350 | /** | ||
351 | * Dequeue a job from the queue. | ||
352 | * | ||
353 | * @param qe handle for the job | ||
354 | */ | ||
355 | void | ||
356 | GNUNET_FS_dequeue_ (struct GNUNET_FS_QueueEntry *qe) | ||
357 | { | ||
358 | struct GNUNET_FS_Handle *h; | ||
359 | |||
360 | h = qe->h; | ||
361 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dequeueing job %p\n", qe); | ||
362 | if (GNUNET_YES == qe->active) | ||
363 | stop_job (qe); | ||
364 | GNUNET_CONTAINER_DLL_remove (h->pending_head, h->pending_tail, qe); | ||
365 | GNUNET_free (qe); | ||
366 | if (NULL != h->queue_job) | ||
367 | GNUNET_SCHEDULER_cancel (h->queue_job); | ||
368 | h->queue_job = GNUNET_SCHEDULER_add_now (&process_job_queue, h); | ||
369 | } | ||
370 | |||
371 | |||
372 | /** | ||
373 | * Create a top-level activity entry. | ||
374 | * | ||
375 | * @param h global fs handle | ||
376 | * @param ssf suspend signal function to use | ||
377 | * @param ssf_cls closure for @a ssf | ||
378 | * @return fresh top-level activity handle | ||
379 | */ | ||
380 | struct TopLevelActivity * | ||
381 | GNUNET_FS_make_top (struct GNUNET_FS_Handle *h, | ||
382 | SuspendSignalFunction ssf, | ||
383 | void *ssf_cls) | ||
384 | { | ||
385 | struct TopLevelActivity *ret; | ||
386 | |||
387 | ret = GNUNET_new (struct TopLevelActivity); | ||
388 | ret->ssf = ssf; | ||
389 | ret->ssf_cls = ssf_cls; | ||
390 | GNUNET_CONTAINER_DLL_insert (h->top_head, h->top_tail, ret); | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | |||
395 | /** | ||
396 | * Destroy a top-level activity entry. | ||
397 | * | ||
398 | * @param h global fs handle | ||
399 | * @param top top level activity entry | ||
400 | */ | ||
401 | void | ||
402 | GNUNET_FS_end_top (struct GNUNET_FS_Handle *h, struct TopLevelActivity *top) | ||
403 | { | ||
404 | GNUNET_CONTAINER_DLL_remove (h->top_head, h->top_tail, top); | ||
405 | GNUNET_free (top); | ||
406 | } | ||
407 | |||
408 | |||
409 | /** | ||
410 | * Closure for #GNUNET_FS_data_reader_file_(). | ||
411 | */ | ||
412 | struct FileInfo | ||
413 | { | ||
414 | /** | ||
415 | * Name of the file to read. | ||
416 | */ | ||
417 | char *filename; | ||
418 | |||
419 | /** | ||
420 | * File descriptor, NULL if it has not yet been opened. | ||
421 | */ | ||
422 | struct GNUNET_DISK_FileHandle *fd; | ||
423 | }; | ||
424 | |||
425 | |||
426 | /** | ||
427 | * Function that provides data by reading from a file. | ||
428 | * | ||
429 | * @param cls closure with the `struct FileInfo *` | ||
430 | * @param offset offset to read from; it is possible | ||
431 | * that the caller might need to go backwards | ||
432 | * a bit at times; set to `UINT64_MAX` to tell | ||
433 | * the reader that we won't be reading for a while | ||
434 | * (used to close the file descriptor but NOT fully | ||
435 | * clean up the reader's state); in this case, | ||
436 | * a value of '0' for @a max should be ignored | ||
437 | * @param max maximum number of bytes that should be | ||
438 | * copied to @a buf; readers are not allowed | ||
439 | * to provide less data unless there is an error; | ||
440 | * a value of "0" will be used at the end to allow | ||
441 | * the reader to clean up its internal state | ||
442 | * @param buf where the reader should write the data | ||
443 | * @param emsg location for the reader to store an error message | ||
444 | * @return number of bytes written, usually @a max, 0 on error | ||
445 | */ | ||
446 | size_t | ||
447 | GNUNET_FS_data_reader_file_ (void *cls, | ||
448 | uint64_t offset, | ||
449 | size_t max, | ||
450 | void *buf, | ||
451 | char **emsg) | ||
452 | { | ||
453 | struct FileInfo *fi = cls; | ||
454 | ssize_t ret; | ||
455 | |||
456 | if (UINT64_MAX == offset) | ||
457 | { | ||
458 | if (NULL != fi->fd) | ||
459 | { | ||
460 | GNUNET_DISK_file_close (fi->fd); | ||
461 | fi->fd = NULL; | ||
462 | } | ||
463 | return 0; | ||
464 | } | ||
465 | if (0 == max) | ||
466 | { | ||
467 | if (NULL != fi->fd) | ||
468 | GNUNET_DISK_file_close (fi->fd); | ||
469 | GNUNET_free (fi->filename); | ||
470 | GNUNET_free (fi); | ||
471 | return 0; | ||
472 | } | ||
473 | if (NULL == fi->fd) | ||
474 | { | ||
475 | fi->fd = GNUNET_DISK_file_open (fi->filename, | ||
476 | GNUNET_DISK_OPEN_READ, | ||
477 | GNUNET_DISK_PERM_NONE); | ||
478 | if (NULL == fi->fd) | ||
479 | { | ||
480 | GNUNET_asprintf (emsg, | ||
481 | _ ("Could not open file `%s': %s"), | ||
482 | fi->filename, | ||
483 | strerror (errno)); | ||
484 | return 0; | ||
485 | } | ||
486 | } | ||
487 | if ((GNUNET_SYSERR == | ||
488 | GNUNET_DISK_file_seek (fi->fd, offset, GNUNET_DISK_SEEK_SET)) || | ||
489 | (-1 == (ret = GNUNET_DISK_file_read (fi->fd, buf, max)))) | ||
490 | { | ||
491 | GNUNET_asprintf (emsg, | ||
492 | _ ("Could not read file `%s': %s"), | ||
493 | fi->filename, | ||
494 | strerror (errno)); | ||
495 | return 0; | ||
496 | } | ||
497 | if (ret != max) | ||
498 | { | ||
499 | GNUNET_asprintf (emsg, | ||
500 | _ ("Short read reading from file `%s'!"), | ||
501 | fi->filename); | ||
502 | return 0; | ||
503 | } | ||
504 | return max; | ||
505 | } | ||
506 | |||
507 | |||
508 | void * | ||
509 | GNUNET_FS_make_file_reader_context_ (const char *filename) | ||
510 | { | ||
511 | struct FileInfo *fi; | ||
512 | |||
513 | fi = GNUNET_new (struct FileInfo); | ||
514 | fi->filename = GNUNET_STRINGS_filename_expand (filename); | ||
515 | if (NULL == fi->filename) | ||
516 | { | ||
517 | GNUNET_free (fi); | ||
518 | return NULL; | ||
519 | } | ||
520 | return fi; | ||
521 | } | ||
522 | |||
523 | |||
524 | /** | ||
525 | * Function that provides data by copying from a buffer. | ||
526 | * | ||
527 | * @param cls closure (points to the buffer) | ||
528 | * @param offset offset to read from; it is possible | ||
529 | * that the caller might need to go backwards | ||
530 | * a bit at times; set to `UINT64_MAX` to tell | ||
531 | * the reader that we won't be reading for a while | ||
532 | * (used to close the file descriptor but NOT fully | ||
533 | * clean up the reader's state); in this case, | ||
534 | * a value of '0' for @a max should be ignored | ||
535 | * @param max maximum number of bytes that should be | ||
536 | * copied to @a buf; readers are not allowed | ||
537 | * to provide less data unless there is an error; | ||
538 | * a value of "0" will be used at the end to allow | ||
539 | * the reader to clean up its internal state | ||
540 | * @param buf where the reader should write the data | ||
541 | * @param emsg location for the reader to store an error message | ||
542 | * @return number of bytes written, usually @a max, 0 on error | ||
543 | */ | ||
544 | size_t | ||
545 | GNUNET_FS_data_reader_copy_ (void *cls, | ||
546 | uint64_t offset, | ||
547 | size_t max, | ||
548 | void *buf, | ||
549 | char **emsg) | ||
550 | { | ||
551 | char *data = cls; | ||
552 | |||
553 | if (UINT64_MAX == offset) | ||
554 | return 0; | ||
555 | if (0 == max) | ||
556 | { | ||
557 | GNUNET_free (data); | ||
558 | return 0; | ||
559 | } | ||
560 | GNUNET_memcpy (buf, &data[offset], max); | ||
561 | return max; | ||
562 | } | ||
563 | |||
564 | |||
565 | /** | ||
566 | * Return the full filename where we would store state information | ||
567 | * (for serialization/deserialization). | ||
568 | * | ||
569 | * @param h master context | ||
570 | * @param ext component of the path | ||
571 | * @param ent entity identifier (or empty string for the directory) | ||
572 | * @return NULL on error | ||
573 | */ | ||
574 | static char * | ||
575 | get_serialization_file_name (struct GNUNET_FS_Handle *h, | ||
576 | const char *ext, | ||
577 | const char *ent) | ||
578 | { | ||
579 | char *basename; | ||
580 | char *ret; | ||
581 | |||
582 | if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE)) | ||
583 | return NULL; /* persistence not requested */ | ||
584 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg, | ||
585 | "fs", | ||
586 | "STATE_DIR", | ||
587 | &basename)) | ||
588 | return NULL; | ||
589 | GNUNET_asprintf (&ret, | ||
590 | "%s%s%s%s%s%s%s", | ||
591 | basename, | ||
592 | DIR_SEPARATOR_STR, | ||
593 | h->client_name, | ||
594 | DIR_SEPARATOR_STR, | ||
595 | ext, | ||
596 | DIR_SEPARATOR_STR, | ||
597 | ent); | ||
598 | GNUNET_free (basename); | ||
599 | return ret; | ||
600 | } | ||
601 | |||
602 | |||
603 | /** | ||
604 | * Return the full filename where we would store state information | ||
605 | * (for serialization/deserialization) that is associated with a | ||
606 | * parent operation. | ||
607 | * | ||
608 | * @param h master context | ||
609 | * @param ext component of the path | ||
610 | * @param uni name of the parent operation | ||
611 | * @param ent entity identifier (or empty string for the directory) | ||
612 | * @return NULL on error | ||
613 | */ | ||
614 | static char * | ||
615 | get_serialization_file_name_in_dir (struct GNUNET_FS_Handle *h, | ||
616 | const char *ext, | ||
617 | const char *uni, | ||
618 | const char *ent) | ||
619 | { | ||
620 | char *basename; | ||
621 | char *ret; | ||
622 | |||
623 | if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE)) | ||
624 | return NULL; /* persistence not requested */ | ||
625 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg, | ||
626 | "fs", | ||
627 | "STATE_DIR", | ||
628 | &basename)) | ||
629 | return NULL; | ||
630 | GNUNET_asprintf (&ret, | ||
631 | "%s%s%s%s%s%s%s.dir%s%s", | ||
632 | basename, | ||
633 | DIR_SEPARATOR_STR, | ||
634 | h->client_name, | ||
635 | DIR_SEPARATOR_STR, | ||
636 | ext, | ||
637 | DIR_SEPARATOR_STR, | ||
638 | uni, | ||
639 | DIR_SEPARATOR_STR, | ||
640 | ent); | ||
641 | GNUNET_free (basename); | ||
642 | return ret; | ||
643 | } | ||
644 | |||
645 | |||
646 | /** | ||
647 | * Return a read handle for deserialization. | ||
648 | * | ||
649 | * @param h master context | ||
650 | * @param ext component of the path | ||
651 | * @param ent entity identifier (or empty string for the directory) | ||
652 | * @return NULL on error | ||
653 | */ | ||
654 | static struct GNUNET_BIO_ReadHandle * | ||
655 | get_read_handle (struct GNUNET_FS_Handle *h, const char *ext, const char *ent) | ||
656 | { | ||
657 | char *fn; | ||
658 | struct GNUNET_BIO_ReadHandle *ret; | ||
659 | |||
660 | fn = get_serialization_file_name (h, ext, ent); | ||
661 | if (NULL == fn) | ||
662 | return NULL; | ||
663 | ret = GNUNET_BIO_read_open_file (fn); | ||
664 | GNUNET_free (fn); | ||
665 | return ret; | ||
666 | } | ||
667 | |||
668 | |||
669 | /** | ||
670 | * Return a write handle for serialization. | ||
671 | * | ||
672 | * @param h master context | ||
673 | * @param ext component of the path | ||
674 | * @param ent entity identifier (or empty string for the directory) | ||
675 | * @return NULL on error | ||
676 | */ | ||
677 | static struct GNUNET_BIO_WriteHandle * | ||
678 | get_write_handle (struct GNUNET_FS_Handle *h, const char *ext, const char *ent) | ||
679 | { | ||
680 | char *fn; | ||
681 | struct GNUNET_BIO_WriteHandle *ret; | ||
682 | |||
683 | fn = get_serialization_file_name (h, ext, ent); | ||
684 | if (NULL == fn) | ||
685 | return NULL; | ||
686 | ret = GNUNET_BIO_write_open_file (fn); | ||
687 | GNUNET_break (NULL != ret); | ||
688 | GNUNET_free (fn); | ||
689 | return ret; | ||
690 | } | ||
691 | |||
692 | |||
693 | /** | ||
694 | * Return a write handle for serialization. | ||
695 | * | ||
696 | * @param h master context | ||
697 | * @param ext component of the path | ||
698 | * @param uni name of parent | ||
699 | * @param ent entity identifier (or empty string for the directory) | ||
700 | * @return NULL on error | ||
701 | */ | ||
702 | static struct GNUNET_BIO_WriteHandle * | ||
703 | get_write_handle_in_dir (struct GNUNET_FS_Handle *h, | ||
704 | const char *ext, | ||
705 | const char *uni, | ||
706 | const char *ent) | ||
707 | { | ||
708 | char *fn; | ||
709 | struct GNUNET_BIO_WriteHandle *ret; | ||
710 | |||
711 | fn = get_serialization_file_name_in_dir (h, ext, uni, ent); | ||
712 | if (NULL == fn) | ||
713 | return NULL; | ||
714 | ret = GNUNET_BIO_write_open_file (fn); | ||
715 | GNUNET_free (fn); | ||
716 | return ret; | ||
717 | } | ||
718 | |||
719 | |||
720 | /** | ||
721 | * Remove serialization/deserialization file from disk. | ||
722 | * | ||
723 | * @param h master context | ||
724 | * @param ext component of the path | ||
725 | * @param ent entity identifier | ||
726 | */ | ||
727 | void | ||
728 | GNUNET_FS_remove_sync_file_ (struct GNUNET_FS_Handle *h, | ||
729 | const char *ext, | ||
730 | const char *ent) | ||
731 | { | ||
732 | char *filename; | ||
733 | |||
734 | if ((NULL == ent) || (0 == strlen (ent))) | ||
735 | { | ||
736 | GNUNET_break (0); | ||
737 | return; | ||
738 | } | ||
739 | filename = get_serialization_file_name (h, ext, ent); | ||
740 | if (NULL != filename) | ||
741 | { | ||
742 | if ((0 != unlink (filename)) && (ENOENT != errno)) | ||
743 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
744 | GNUNET_free (filename); | ||
745 | } | ||
746 | } | ||
747 | |||
748 | |||
749 | /** | ||
750 | * Remove serialization/deserialization file from disk. | ||
751 | * | ||
752 | * @param h master context | ||
753 | * @param ext component of the path | ||
754 | * @param uni parent name | ||
755 | * @param ent entity identifier | ||
756 | */ | ||
757 | static void | ||
758 | remove_sync_file_in_dir (struct GNUNET_FS_Handle *h, | ||
759 | const char *ext, | ||
760 | const char *uni, | ||
761 | const char *ent) | ||
762 | { | ||
763 | char *filename; | ||
764 | |||
765 | if ((NULL == ent) || (0 == strlen (ent))) | ||
766 | { | ||
767 | GNUNET_break (0); | ||
768 | return; | ||
769 | } | ||
770 | filename = get_serialization_file_name_in_dir (h, ext, uni, ent); | ||
771 | if (NULL == filename) | ||
772 | return; | ||
773 | if (0 != unlink (filename)) | ||
774 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
775 | GNUNET_free (filename); | ||
776 | } | ||
777 | |||
778 | |||
779 | /** | ||
780 | * Remove serialization/deserialization directory from disk. | ||
781 | * | ||
782 | * @param h master context | ||
783 | * @param ext component of the path | ||
784 | * @param uni unique name of parent | ||
785 | */ | ||
786 | void | ||
787 | GNUNET_FS_remove_sync_dir_ (struct GNUNET_FS_Handle *h, | ||
788 | const char *ext, | ||
789 | const char *uni) | ||
790 | { | ||
791 | char *dn; | ||
792 | |||
793 | if (NULL == uni) | ||
794 | return; | ||
795 | dn = get_serialization_file_name_in_dir (h, ext, uni, ""); | ||
796 | if (NULL == dn) | ||
797 | return; | ||
798 | if ((GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES)) && | ||
799 | (GNUNET_OK != GNUNET_DISK_directory_remove (dn))) | ||
800 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dn); | ||
801 | GNUNET_free (dn); | ||
802 | } | ||
803 | |||
804 | |||
805 | /** | ||
806 | * Serialize a start-time. Since we use start-times to | ||
807 | * calculate the duration of some operation, we actually | ||
808 | * do not serialize the absolute time but the (relative) | ||
809 | * duration since the start time. When we then | ||
810 | * deserialize the start time, we take the current time and | ||
811 | * subtract that duration so that we get again an absolute | ||
812 | * time stamp that will result in correct performance | ||
813 | * calculations. | ||
814 | * | ||
815 | * @param wh handle for writing | ||
816 | * @param timestamp time to serialize | ||
817 | * @return #GNUNET_OK on success | ||
818 | */ | ||
819 | static int | ||
820 | write_start_time (struct GNUNET_BIO_WriteHandle *wh, | ||
821 | struct GNUNET_TIME_Absolute timestamp) | ||
822 | { | ||
823 | struct GNUNET_TIME_Relative dur; | ||
824 | |||
825 | dur = GNUNET_TIME_absolute_get_duration (timestamp); | ||
826 | return GNUNET_BIO_write_int64 (wh, "start time", dur.rel_value_us); | ||
827 | } | ||
828 | |||
829 | |||
830 | /** | ||
831 | * Deserialize a start-time. Since we use start-times to | ||
832 | * calculate the duration of some operation, we actually | ||
833 | * do not serialize the absolute time but the (relative) | ||
834 | * duration since the start time. Thus, when we then | ||
835 | * deserialize the start time, we take the current time and | ||
836 | * subtract that duration so that we get again an absolute | ||
837 | * time stamp that will result in correct performance | ||
838 | * calculations. | ||
839 | * | ||
840 | * @param rh handle for reading | ||
841 | * @param timestamp where to write the deserialized timestamp | ||
842 | * @return #GNUNET_OK on success | ||
843 | */ | ||
844 | static int | ||
845 | read_start_time (struct GNUNET_BIO_ReadHandle *rh, | ||
846 | struct GNUNET_TIME_Absolute *timestamp) | ||
847 | { | ||
848 | struct GNUNET_TIME_Relative dur; | ||
849 | |||
850 | if (GNUNET_OK != GNUNET_BIO_read_int64 (rh, "start time", | ||
851 | (int64_t *) &dur.rel_value_us)) | ||
852 | return GNUNET_SYSERR; | ||
853 | *timestamp = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (), dur); | ||
854 | return GNUNET_OK; | ||
855 | } | ||
856 | |||
857 | |||
858 | /** | ||
859 | * Using the given serialization filename, try to deserialize | ||
860 | * the file-information tree associated with it. | ||
861 | * | ||
862 | * @param h master context | ||
863 | * @param filename name of the file (without directory) with | ||
864 | * the information | ||
865 | * @return NULL on error | ||
866 | */ | ||
867 | static struct GNUNET_FS_FileInformation * | ||
868 | deserialize_file_information (struct GNUNET_FS_Handle *h, const char *filename); | ||
869 | |||
870 | |||
871 | /** | ||
872 | * Using the given serialization filename, try to deserialize | ||
873 | * the file-information tree associated with it. | ||
874 | * | ||
875 | * @param h master context | ||
876 | * @param fn name of the file (without directory) with | ||
877 | * the information | ||
878 | * @param rh handle for reading | ||
879 | * @return NULL on error | ||
880 | */ | ||
881 | static struct GNUNET_FS_FileInformation * | ||
882 | deserialize_fi_node (struct GNUNET_FS_Handle *h, | ||
883 | const char *fn, | ||
884 | struct GNUNET_BIO_ReadHandle *rh) | ||
885 | { | ||
886 | struct GNUNET_FS_FileInformation *ret; | ||
887 | struct GNUNET_FS_FileInformation *nxt; | ||
888 | char b; | ||
889 | char *ksks; | ||
890 | char *chks; | ||
891 | char *skss; | ||
892 | char *filename; | ||
893 | uint32_t dsize; | ||
894 | |||
895 | if (GNUNET_OK != GNUNET_BIO_read (rh, "status flag", &b, sizeof(b))) | ||
896 | { | ||
897 | GNUNET_break (0); | ||
898 | return NULL; | ||
899 | } | ||
900 | ret = GNUNET_new (struct GNUNET_FS_FileInformation); | ||
901 | ret->h = h; | ||
902 | ksks = NULL; | ||
903 | chks = NULL; | ||
904 | skss = NULL; | ||
905 | filename = NULL; | ||
906 | if ((GNUNET_OK != GNUNET_FS_read_meta_data (rh, "metadata", &ret->meta)) || | ||
907 | (GNUNET_OK != GNUNET_BIO_read_string (rh, "ksk-uri", &ksks, 32 * 1024)) || | ||
908 | ((NULL != ksks) && | ||
909 | ((NULL == (ret->keywords = GNUNET_FS_uri_parse (ksks, NULL))) || | ||
910 | (GNUNET_YES != GNUNET_FS_uri_test_ksk (ret->keywords)))) || | ||
911 | (GNUNET_OK != GNUNET_BIO_read_string (rh, "chk-uri", &chks, 1024)) || | ||
912 | ((NULL != chks) && | ||
913 | ((NULL == (ret->chk_uri = GNUNET_FS_uri_parse (chks, NULL))) || | ||
914 | (GNUNET_YES != GNUNET_FS_uri_test_chk (ret->chk_uri)))) || | ||
915 | (GNUNET_OK != GNUNET_BIO_read_string (rh, "sks-uri", &skss, 1024)) || | ||
916 | ((NULL != skss) && | ||
917 | ((NULL == (ret->sks_uri = GNUNET_FS_uri_parse (skss, NULL))) || | ||
918 | (GNUNET_YES != GNUNET_FS_uri_test_sks (ret->sks_uri)))) || | ||
919 | (GNUNET_OK != read_start_time (rh, &ret->start_time)) || | ||
920 | (GNUNET_OK != | ||
921 | GNUNET_BIO_read_string (rh, "emsg", &ret->emsg, 16 * 1024)) || | ||
922 | (GNUNET_OK != | ||
923 | GNUNET_BIO_read_string (rh, "fn", &ret->filename, 16 * 1024)) || | ||
924 | (GNUNET_OK != | ||
925 | GNUNET_BIO_read_int64 ( | ||
926 | rh, | ||
927 | "expiration time", | ||
928 | (int64_t *) &ret->bo.expiration_time.abs_value_us)) || | ||
929 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
930 | rh, | ||
931 | "anonymity level", | ||
932 | (int32_t *) &ret->bo.anonymity_level)) || | ||
933 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
934 | rh, | ||
935 | "content priority", | ||
936 | (int32_t *) &ret->bo.content_priority)) || | ||
937 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
938 | rh, | ||
939 | "replication level", | ||
940 | (int32_t *) &ret->bo.replication_level))) | ||
941 | { | ||
942 | GNUNET_break (0); | ||
943 | goto cleanup; | ||
944 | } | ||
945 | switch (b) | ||
946 | { | ||
947 | case 0: /* file-insert */ | ||
948 | if (GNUNET_OK != GNUNET_BIO_read_int64 ( | ||
949 | rh, | ||
950 | "file size", | ||
951 | (int64_t *) &ret->data.file.file_size)) | ||
952 | { | ||
953 | GNUNET_break (0); | ||
954 | goto cleanup; | ||
955 | } | ||
956 | ret->is_directory = GNUNET_NO; | ||
957 | ret->data.file.do_index = GNUNET_NO; | ||
958 | ret->data.file.have_hash = GNUNET_NO; | ||
959 | ret->data.file.index_start_confirmed = GNUNET_NO; | ||
960 | if (GNUNET_NO == ret->is_published) | ||
961 | { | ||
962 | if (NULL == ret->filename) | ||
963 | { | ||
964 | ret->data.file.reader = &GNUNET_FS_data_reader_copy_; | ||
965 | ret->data.file.reader_cls = | ||
966 | GNUNET_malloc_large (ret->data.file.file_size); | ||
967 | if (ret->data.file.reader_cls == NULL) | ||
968 | goto cleanup; | ||
969 | if (GNUNET_OK != GNUNET_BIO_read (rh, | ||
970 | "file-data", | ||
971 | ret->data.file.reader_cls, | ||
972 | ret->data.file.file_size)) | ||
973 | { | ||
974 | GNUNET_break (0); | ||
975 | goto cleanup; | ||
976 | } | ||
977 | } | ||
978 | else | ||
979 | { | ||
980 | ret->data.file.reader = &GNUNET_FS_data_reader_file_; | ||
981 | ret->data.file.reader_cls = | ||
982 | GNUNET_FS_make_file_reader_context_ (ret->filename); | ||
983 | } | ||
984 | } | ||
985 | break; | ||
986 | |||
987 | case 1: /* file-index, no hash */ | ||
988 | if (NULL == ret->filename) | ||
989 | { | ||
990 | GNUNET_break (0); | ||
991 | goto cleanup; | ||
992 | } | ||
993 | if (GNUNET_OK != GNUNET_BIO_read_int64 ( | ||
994 | rh, | ||
995 | "file size", | ||
996 | (int64_t *) &ret->data.file.file_size)) | ||
997 | { | ||
998 | GNUNET_break (0); | ||
999 | goto cleanup; | ||
1000 | } | ||
1001 | ret->is_directory = GNUNET_NO; | ||
1002 | ret->data.file.do_index = GNUNET_YES; | ||
1003 | ret->data.file.have_hash = GNUNET_NO; | ||
1004 | ret->data.file.index_start_confirmed = GNUNET_NO; | ||
1005 | ret->data.file.reader = &GNUNET_FS_data_reader_file_; | ||
1006 | ret->data.file.reader_cls = | ||
1007 | GNUNET_FS_make_file_reader_context_ (ret->filename); | ||
1008 | break; | ||
1009 | |||
1010 | case 2: /* file-index-with-hash */ | ||
1011 | if (NULL == ret->filename) | ||
1012 | { | ||
1013 | GNUNET_break (0); | ||
1014 | goto cleanup; | ||
1015 | } | ||
1016 | if ((GNUNET_OK != GNUNET_BIO_read_int64 ( | ||
1017 | rh, | ||
1018 | "file size", | ||
1019 | (int64_t *) &ret->data.file.file_size)) || | ||
1020 | (GNUNET_OK != GNUNET_BIO_read (rh, | ||
1021 | "fileid", | ||
1022 | &ret->data.file.file_id, | ||
1023 | sizeof(struct GNUNET_HashCode)))) | ||
1024 | { | ||
1025 | GNUNET_break (0); | ||
1026 | goto cleanup; | ||
1027 | } | ||
1028 | ret->is_directory = GNUNET_NO; | ||
1029 | ret->data.file.do_index = GNUNET_YES; | ||
1030 | ret->data.file.have_hash = GNUNET_YES; | ||
1031 | ret->data.file.index_start_confirmed = GNUNET_NO; | ||
1032 | ret->data.file.reader = &GNUNET_FS_data_reader_file_; | ||
1033 | ret->data.file.reader_cls = | ||
1034 | GNUNET_FS_make_file_reader_context_ (ret->filename); | ||
1035 | break; | ||
1036 | |||
1037 | case 3: /* file-index-with-hash-confirmed */ | ||
1038 | if (NULL == ret->filename) | ||
1039 | { | ||
1040 | GNUNET_break (0); | ||
1041 | goto cleanup; | ||
1042 | } | ||
1043 | if ((GNUNET_OK != GNUNET_BIO_read_int64 ( | ||
1044 | rh, | ||
1045 | "file size", | ||
1046 | (int64_t *) &ret->data.file.file_size)) || | ||
1047 | (GNUNET_OK != GNUNET_BIO_read (rh, | ||
1048 | "fileid", | ||
1049 | &ret->data.file.file_id, | ||
1050 | sizeof(struct GNUNET_HashCode)))) | ||
1051 | { | ||
1052 | GNUNET_break (0); | ||
1053 | goto cleanup; | ||
1054 | } | ||
1055 | ret->is_directory = GNUNET_NO; | ||
1056 | ret->data.file.do_index = GNUNET_YES; | ||
1057 | ret->data.file.have_hash = GNUNET_YES; | ||
1058 | ret->data.file.index_start_confirmed = GNUNET_YES; | ||
1059 | ret->data.file.reader = &GNUNET_FS_data_reader_file_; | ||
1060 | ret->data.file.reader_cls = | ||
1061 | GNUNET_FS_make_file_reader_context_ (ret->filename); | ||
1062 | break; | ||
1063 | |||
1064 | case 4: /* directory */ | ||
1065 | ret->is_directory = GNUNET_YES; | ||
1066 | if ((GNUNET_OK != GNUNET_BIO_read_int32 (rh, "dsize", | ||
1067 | (int32_t *) &dsize)) || | ||
1068 | (GNUNET_OK != | ||
1069 | GNUNET_BIO_read_int64 ( | ||
1070 | rh, | ||
1071 | "contents completed", | ||
1072 | (int64_t *) &ret->data.dir.contents_completed)) || | ||
1073 | (GNUNET_OK != | ||
1074 | GNUNET_BIO_read_int64 ( | ||
1075 | rh, | ||
1076 | "contents size", | ||
1077 | (int64_t *) &ret->data.dir.contents_size)) || | ||
1078 | (NULL == (ret->data.dir.dir_data = GNUNET_malloc_large (dsize))) || | ||
1079 | (GNUNET_OK != | ||
1080 | GNUNET_BIO_read (rh, "dir-data", ret->data.dir.dir_data, dsize)) || | ||
1081 | (GNUNET_OK != | ||
1082 | GNUNET_BIO_read_string (rh, "ent-filename", &filename, 16 * 1024))) | ||
1083 | { | ||
1084 | GNUNET_break (0); | ||
1085 | goto cleanup; | ||
1086 | } | ||
1087 | ret->data.dir.dir_size = (uint32_t) dsize; | ||
1088 | if (NULL != filename) | ||
1089 | { | ||
1090 | ret->data.dir.entries = deserialize_file_information (h, filename); | ||
1091 | GNUNET_free (filename); | ||
1092 | filename = NULL; | ||
1093 | nxt = ret->data.dir.entries; | ||
1094 | while (NULL != nxt) | ||
1095 | { | ||
1096 | nxt->dir = ret; | ||
1097 | nxt = nxt->next; | ||
1098 | } | ||
1099 | } | ||
1100 | break; | ||
1101 | |||
1102 | default: | ||
1103 | GNUNET_break (0); | ||
1104 | goto cleanup; | ||
1105 | } | ||
1106 | ret->serialization = GNUNET_strdup (fn); | ||
1107 | if (GNUNET_OK != | ||
1108 | GNUNET_BIO_read_string (rh, "nxt-filename", &filename, 16 * 1024)) | ||
1109 | { | ||
1110 | GNUNET_break (0); | ||
1111 | goto cleanup; | ||
1112 | } | ||
1113 | if (NULL != filename) | ||
1114 | { | ||
1115 | ret->next = deserialize_file_information (h, filename); | ||
1116 | GNUNET_free (filename); | ||
1117 | filename = NULL; | ||
1118 | } | ||
1119 | GNUNET_free (ksks); | ||
1120 | GNUNET_free (skss); | ||
1121 | GNUNET_free (chks); | ||
1122 | return ret; | ||
1123 | cleanup: | ||
1124 | GNUNET_free (ksks); | ||
1125 | GNUNET_free (chks); | ||
1126 | GNUNET_free (skss); | ||
1127 | GNUNET_free (filename); | ||
1128 | GNUNET_FS_file_information_destroy (ret, NULL, NULL); | ||
1129 | return NULL; | ||
1130 | } | ||
1131 | |||
1132 | |||
1133 | /** | ||
1134 | * Using the given serialization filename, try to deserialize | ||
1135 | * the file-information tree associated with it. | ||
1136 | * | ||
1137 | * @param h master context | ||
1138 | * @param filename name of the file (without directory) with | ||
1139 | * the information | ||
1140 | * @return NULL on error | ||
1141 | */ | ||
1142 | static struct GNUNET_FS_FileInformation * | ||
1143 | deserialize_file_information (struct GNUNET_FS_Handle *h, const char *filename) | ||
1144 | { | ||
1145 | struct GNUNET_FS_FileInformation *ret; | ||
1146 | struct GNUNET_BIO_ReadHandle *rh; | ||
1147 | char *emsg; | ||
1148 | char *fn; | ||
1149 | |||
1150 | rh = get_read_handle (h, GNUNET_FS_SYNC_PATH_FILE_INFO, filename); | ||
1151 | if (NULL == rh) | ||
1152 | return NULL; | ||
1153 | ret = deserialize_fi_node (h, filename, rh); | ||
1154 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
1155 | { | ||
1156 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1157 | _ ("Failed to resume publishing information `%s': %s\n"), | ||
1158 | filename, | ||
1159 | emsg); | ||
1160 | GNUNET_free (emsg); | ||
1161 | } | ||
1162 | if (NULL == ret) | ||
1163 | { | ||
1164 | fn = | ||
1165 | get_serialization_file_name (h, GNUNET_FS_SYNC_PATH_FILE_INFO, filename); | ||
1166 | if (NULL != fn) | ||
1167 | { | ||
1168 | if (0 != unlink (fn)) | ||
1169 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); | ||
1170 | GNUNET_free (fn); | ||
1171 | } | ||
1172 | } | ||
1173 | return ret; | ||
1174 | } | ||
1175 | |||
1176 | |||
1177 | /** | ||
1178 | * Given a serialization name (full absolute path), return the | ||
1179 | * basename of the file (without the path), which must only | ||
1180 | * consist of the 6 random characters. | ||
1181 | * | ||
1182 | * @param fullname name to extract the basename from | ||
1183 | * @return copy of the basename, NULL on error | ||
1184 | */ | ||
1185 | static char * | ||
1186 | get_serialization_short_name (const char *fullname) | ||
1187 | { | ||
1188 | const char *end; | ||
1189 | const char *nxt; | ||
1190 | |||
1191 | end = NULL; | ||
1192 | nxt = fullname; | ||
1193 | /* FIXME: we could do this faster since we know | ||
1194 | * the length of 'end'... */ | ||
1195 | while ('\0' != *nxt) | ||
1196 | { | ||
1197 | if (DIR_SEPARATOR == *nxt) | ||
1198 | end = nxt + 1; | ||
1199 | nxt++; | ||
1200 | } | ||
1201 | if ((NULL == end) || (0 == strlen (end))) | ||
1202 | { | ||
1203 | GNUNET_break (0); | ||
1204 | return NULL; | ||
1205 | } | ||
1206 | GNUNET_break (6 == strlen (end)); | ||
1207 | return GNUNET_strdup (end); | ||
1208 | } | ||
1209 | |||
1210 | |||
1211 | /** | ||
1212 | * Create a new random name for serialization. Also checks if persistence | ||
1213 | * is enabled and returns NULL if not. | ||
1214 | * | ||
1215 | * @param h master context | ||
1216 | * @param ext component of the path | ||
1217 | * @return NULL on error | ||
1218 | */ | ||
1219 | static char * | ||
1220 | make_serialization_file_name (struct GNUNET_FS_Handle *h, const char *ext) | ||
1221 | { | ||
1222 | char *fn; | ||
1223 | char *dn; | ||
1224 | char *ret; | ||
1225 | |||
1226 | if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE)) | ||
1227 | return NULL; /* persistence not requested */ | ||
1228 | dn = get_serialization_file_name (h, ext, ""); | ||
1229 | if (NULL == dn) | ||
1230 | return NULL; | ||
1231 | if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dn)) | ||
1232 | { | ||
1233 | GNUNET_free (dn); | ||
1234 | return NULL; | ||
1235 | } | ||
1236 | fn = GNUNET_DISK_mktemp (dn); | ||
1237 | GNUNET_free (dn); | ||
1238 | if (NULL == fn) | ||
1239 | return NULL; /* epic fail */ | ||
1240 | ret = get_serialization_short_name (fn); | ||
1241 | GNUNET_free (fn); | ||
1242 | return ret; | ||
1243 | } | ||
1244 | |||
1245 | |||
1246 | /** | ||
1247 | * Create a new random name for serialization. Also checks if persistence | ||
1248 | * is enabled and returns NULL if not. | ||
1249 | * | ||
1250 | * @param h master context | ||
1251 | * @param ext component of the path | ||
1252 | * @param uni name of parent | ||
1253 | * @return NULL on error | ||
1254 | */ | ||
1255 | static char * | ||
1256 | make_serialization_file_name_in_dir (struct GNUNET_FS_Handle *h, | ||
1257 | const char *ext, | ||
1258 | const char *uni) | ||
1259 | { | ||
1260 | char *fn; | ||
1261 | char *dn; | ||
1262 | char *ret; | ||
1263 | |||
1264 | if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE)) | ||
1265 | return NULL; /* persistence not requested */ | ||
1266 | dn = get_serialization_file_name_in_dir (h, ext, uni, ""); | ||
1267 | if (NULL == dn) | ||
1268 | return NULL; | ||
1269 | if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dn)) | ||
1270 | { | ||
1271 | GNUNET_free (dn); | ||
1272 | return NULL; | ||
1273 | } | ||
1274 | fn = GNUNET_DISK_mktemp (dn); | ||
1275 | GNUNET_free (dn); | ||
1276 | if (NULL == fn) | ||
1277 | return NULL; /* epic fail */ | ||
1278 | ret = get_serialization_short_name (fn); | ||
1279 | GNUNET_free (fn); | ||
1280 | return ret; | ||
1281 | } | ||
1282 | |||
1283 | |||
1284 | /** | ||
1285 | * Copy all of the data from the reader to the write handle. | ||
1286 | * | ||
1287 | * @param wh write handle | ||
1288 | * @param fi file with reader | ||
1289 | * @return #GNUNET_OK on success | ||
1290 | */ | ||
1291 | static int | ||
1292 | copy_from_reader (struct GNUNET_BIO_WriteHandle *wh, | ||
1293 | struct GNUNET_FS_FileInformation *fi) | ||
1294 | { | ||
1295 | char buf[32 * 1024]; | ||
1296 | uint64_t off; | ||
1297 | size_t ret; | ||
1298 | size_t left; | ||
1299 | char *emsg; | ||
1300 | |||
1301 | emsg = NULL; | ||
1302 | off = 0; | ||
1303 | while (off < fi->data.file.file_size) | ||
1304 | { | ||
1305 | left = GNUNET_MIN (sizeof(buf), fi->data.file.file_size - off); | ||
1306 | ret = | ||
1307 | fi->data.file.reader (fi->data.file.reader_cls, off, left, buf, &emsg); | ||
1308 | if (0 == ret) | ||
1309 | { | ||
1310 | GNUNET_free (emsg); | ||
1311 | return GNUNET_SYSERR; | ||
1312 | } | ||
1313 | if (GNUNET_OK != GNUNET_BIO_write (wh, "copied from reader", buf, ret)) | ||
1314 | return GNUNET_SYSERR; | ||
1315 | off += ret; | ||
1316 | } | ||
1317 | return GNUNET_OK; | ||
1318 | } | ||
1319 | |||
1320 | |||
1321 | /** | ||
1322 | * Create a temporary file on disk to store the current | ||
1323 | * state of @a fi in. | ||
1324 | * | ||
1325 | * @param fi file information to sync with disk | ||
1326 | */ | ||
1327 | void | ||
1328 | GNUNET_FS_file_information_sync_ (struct GNUNET_FS_FileInformation *fi) | ||
1329 | { | ||
1330 | char *fn; | ||
1331 | struct GNUNET_BIO_WriteHandle *wh; | ||
1332 | char b; | ||
1333 | char *ksks; | ||
1334 | char *chks; | ||
1335 | char *skss; | ||
1336 | |||
1337 | if (NULL == fi->serialization) | ||
1338 | fi->serialization = | ||
1339 | make_serialization_file_name (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO); | ||
1340 | if (NULL == fi->serialization) | ||
1341 | return; | ||
1342 | wh = | ||
1343 | get_write_handle (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO, fi->serialization); | ||
1344 | if (NULL == wh) | ||
1345 | { | ||
1346 | GNUNET_free (fi->serialization); | ||
1347 | fi->serialization = NULL; | ||
1348 | return; | ||
1349 | } | ||
1350 | if (GNUNET_YES == fi->is_directory) | ||
1351 | b = 4; | ||
1352 | else if (GNUNET_YES == fi->data.file.index_start_confirmed) | ||
1353 | b = 3; | ||
1354 | else if (GNUNET_YES == fi->data.file.have_hash) | ||
1355 | b = 2; | ||
1356 | else if (GNUNET_YES == fi->data.file.do_index) | ||
1357 | b = 1; | ||
1358 | else | ||
1359 | b = 0; | ||
1360 | if (NULL != fi->keywords) | ||
1361 | ksks = GNUNET_FS_uri_to_string (fi->keywords); | ||
1362 | else | ||
1363 | ksks = NULL; | ||
1364 | if (NULL != fi->chk_uri) | ||
1365 | chks = GNUNET_FS_uri_to_string (fi->chk_uri); | ||
1366 | else | ||
1367 | chks = NULL; | ||
1368 | if (NULL != fi->sks_uri) | ||
1369 | skss = GNUNET_FS_uri_to_string (fi->sks_uri); | ||
1370 | else | ||
1371 | skss = NULL; | ||
1372 | struct GNUNET_BIO_WriteSpec ws1[] = { | ||
1373 | GNUNET_BIO_write_spec_object ("b", &b, sizeof (b)), | ||
1374 | GNUNET_FS_write_spec_meta_data ("meta", fi->meta), | ||
1375 | GNUNET_BIO_write_spec_string ("ksks", ksks), | ||
1376 | GNUNET_BIO_write_spec_string ("chks", chks), | ||
1377 | GNUNET_BIO_write_spec_string ("skss", skss), | ||
1378 | GNUNET_BIO_write_spec_end (), | ||
1379 | }; | ||
1380 | struct GNUNET_BIO_WriteSpec ws2[] = { | ||
1381 | GNUNET_BIO_write_spec_string ("emsg", fi->emsg), | ||
1382 | GNUNET_BIO_write_spec_string ("filename", fi->filename), | ||
1383 | GNUNET_BIO_write_spec_int64 ( | ||
1384 | "expiration time", | ||
1385 | (int64_t *) &fi->bo.expiration_time.abs_value_us), | ||
1386 | GNUNET_BIO_write_spec_int32 ( | ||
1387 | "anonymity level", | ||
1388 | (int32_t *) &fi->bo.anonymity_level), | ||
1389 | GNUNET_BIO_write_spec_int32 ( | ||
1390 | "content priority", | ||
1391 | (int32_t *) &fi->bo.content_priority), | ||
1392 | GNUNET_BIO_write_spec_int32 ( | ||
1393 | "replication level", | ||
1394 | (int32_t *) &fi->bo.replication_level), | ||
1395 | GNUNET_BIO_write_spec_end (), | ||
1396 | }; | ||
1397 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws1)) || | ||
1398 | (GNUNET_OK != write_start_time (wh, fi->start_time)) || | ||
1399 | (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws2))) | ||
1400 | { | ||
1401 | GNUNET_break (0); | ||
1402 | goto cleanup; | ||
1403 | } | ||
1404 | GNUNET_free (chks); | ||
1405 | chks = NULL; | ||
1406 | GNUNET_free (ksks); | ||
1407 | ksks = NULL; | ||
1408 | GNUNET_free (skss); | ||
1409 | skss = NULL; | ||
1410 | |||
1411 | switch (b) | ||
1412 | { | ||
1413 | case 0: /* file-insert */ | ||
1414 | if (GNUNET_OK != GNUNET_BIO_write_int64 (wh, "file size", | ||
1415 | fi->data.file.file_size)) | ||
1416 | { | ||
1417 | GNUNET_break (0); | ||
1418 | goto cleanup; | ||
1419 | } | ||
1420 | if ((GNUNET_NO == fi->is_published) && (NULL == fi->filename)) | ||
1421 | if (GNUNET_OK != copy_from_reader (wh, fi)) | ||
1422 | { | ||
1423 | GNUNET_break (0); | ||
1424 | goto cleanup; | ||
1425 | } | ||
1426 | break; | ||
1427 | |||
1428 | case 1: /* file-index, no hash */ | ||
1429 | if (NULL == fi->filename) | ||
1430 | { | ||
1431 | GNUNET_break (0); | ||
1432 | goto cleanup; | ||
1433 | } | ||
1434 | if (GNUNET_OK != GNUNET_BIO_write_int64 (wh, "file size", | ||
1435 | fi->data.file.file_size)) | ||
1436 | { | ||
1437 | GNUNET_break (0); | ||
1438 | goto cleanup; | ||
1439 | } | ||
1440 | break; | ||
1441 | |||
1442 | case 2: /* file-index-with-hash */ | ||
1443 | case 3: /* file-index-with-hash-confirmed */ | ||
1444 | if (NULL == fi->filename) | ||
1445 | { | ||
1446 | GNUNET_break (0); | ||
1447 | goto cleanup; | ||
1448 | } | ||
1449 | if ((GNUNET_OK != GNUNET_BIO_write_int64 (wh, "file size", | ||
1450 | fi->data.file.file_size)) || | ||
1451 | (GNUNET_OK != GNUNET_BIO_write (wh, | ||
1452 | "file id", | ||
1453 | &fi->data.file.file_id, | ||
1454 | sizeof(struct GNUNET_HashCode)))) | ||
1455 | { | ||
1456 | GNUNET_break (0); | ||
1457 | goto cleanup; | ||
1458 | } | ||
1459 | break; | ||
1460 | |||
1461 | case 4: /* directory */ | ||
1462 | if ((NULL != fi->data.dir.entries) && | ||
1463 | (NULL == fi->data.dir.entries->serialization)) | ||
1464 | GNUNET_FS_file_information_sync_ (fi->data.dir.entries); | ||
1465 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
1466 | GNUNET_BIO_write_spec_int32 ("dir size", | ||
1467 | (int32_t *) &fi->data.dir.dir_size), | ||
1468 | GNUNET_BIO_write_spec_int64 ( | ||
1469 | "contents completed", | ||
1470 | (int64_t *) &fi->data.dir.contents_completed), | ||
1471 | GNUNET_BIO_write_spec_int64 ("contents size", | ||
1472 | (int64_t *) &fi->data.dir.contents_size), | ||
1473 | GNUNET_BIO_write_spec_object ("dir data", | ||
1474 | fi->data.dir.dir_data, | ||
1475 | (uint32_t) fi->data.dir.dir_size), | ||
1476 | GNUNET_BIO_write_spec_string ("dir entries", | ||
1477 | (fi->data.dir.entries == NULL) | ||
1478 | ? NULL | ||
1479 | : fi->data.dir.entries->serialization), | ||
1480 | GNUNET_BIO_write_spec_end (), | ||
1481 | }; | ||
1482 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))) | ||
1483 | { | ||
1484 | GNUNET_break (0); | ||
1485 | goto cleanup; | ||
1486 | } | ||
1487 | break; | ||
1488 | |||
1489 | default: | ||
1490 | GNUNET_assert (0); | ||
1491 | goto cleanup; | ||
1492 | } | ||
1493 | if ((NULL != fi->next) && (NULL == fi->next->serialization)) | ||
1494 | GNUNET_FS_file_information_sync_ (fi->next); | ||
1495 | if (GNUNET_OK != GNUNET_BIO_write_string (wh, | ||
1496 | "serialization", | ||
1497 | (fi->next != NULL) | ||
1498 | ? fi->next->serialization | ||
1499 | : NULL)) | ||
1500 | { | ||
1501 | GNUNET_break (0); | ||
1502 | goto cleanup; | ||
1503 | } | ||
1504 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
1505 | { | ||
1506 | wh = NULL; | ||
1507 | GNUNET_break (0); | ||
1508 | goto cleanup; | ||
1509 | } | ||
1510 | return; /* done! */ | ||
1511 | cleanup: | ||
1512 | if (NULL != wh) | ||
1513 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
1514 | GNUNET_free (chks); | ||
1515 | GNUNET_free (ksks); | ||
1516 | GNUNET_free (skss); | ||
1517 | fn = get_serialization_file_name (fi->h, | ||
1518 | GNUNET_FS_SYNC_PATH_FILE_INFO, | ||
1519 | fi->serialization); | ||
1520 | if (NULL != fn) | ||
1521 | { | ||
1522 | if (0 != unlink (fn)) | ||
1523 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); | ||
1524 | GNUNET_free (fn); | ||
1525 | } | ||
1526 | GNUNET_free (fi->serialization); | ||
1527 | fi->serialization = NULL; | ||
1528 | } | ||
1529 | |||
1530 | |||
1531 | /** | ||
1532 | * Find the entry in the file information struct where the | ||
1533 | * serialization filename matches the given name. | ||
1534 | * | ||
1535 | * @param pos file information to search | ||
1536 | * @param srch filename to search for | ||
1537 | * @return NULL if srch was not found in this subtree | ||
1538 | */ | ||
1539 | static struct GNUNET_FS_FileInformation * | ||
1540 | find_file_position (struct GNUNET_FS_FileInformation *pos, const char *srch) | ||
1541 | { | ||
1542 | struct GNUNET_FS_FileInformation *r; | ||
1543 | |||
1544 | while (NULL != pos) | ||
1545 | { | ||
1546 | if (0 == strcmp (srch, pos->serialization)) | ||
1547 | return pos; | ||
1548 | if ((GNUNET_YES == pos->is_directory) && | ||
1549 | (NULL != (r = find_file_position (pos->data.dir.entries, srch)))) | ||
1550 | return r; | ||
1551 | pos = pos->next; | ||
1552 | } | ||
1553 | return NULL; | ||
1554 | } | ||
1555 | |||
1556 | |||
1557 | /** | ||
1558 | * Signal the FS's progress function that we are resuming | ||
1559 | * an upload. | ||
1560 | * | ||
1561 | * @param cls closure (of type `struct GNUNET_FS_PublishContext *`, for the parent (!)) | ||
1562 | * @param fi the entry in the publish-structure | ||
1563 | * @param length length of the file or directory | ||
1564 | * @param meta metadata for the file or directory (can be modified) | ||
1565 | * @param uri pointer to the keywords that will be used for this entry (can be modified) | ||
1566 | * @param bo block options (can be modified) | ||
1567 | * @param do_index should we index? | ||
1568 | * @param client_info pointer to client context set upon creation (can be modified) | ||
1569 | * @return #GNUNET_OK to continue (always) | ||
1570 | */ | ||
1571 | static int | ||
1572 | fip_signal_resume (void *cls, | ||
1573 | struct GNUNET_FS_FileInformation *fi, | ||
1574 | uint64_t length, | ||
1575 | struct GNUNET_FS_MetaData *meta, | ||
1576 | struct GNUNET_FS_Uri **uri, | ||
1577 | struct GNUNET_FS_BlockOptions *bo, | ||
1578 | int *do_index, | ||
1579 | void **client_info) | ||
1580 | { | ||
1581 | struct GNUNET_FS_PublishContext *pc = cls; | ||
1582 | struct GNUNET_FS_ProgressInfo pi; | ||
1583 | |||
1584 | if (GNUNET_YES == pc->skip_next_fi_callback) | ||
1585 | { | ||
1586 | pc->skip_next_fi_callback = GNUNET_NO; | ||
1587 | return GNUNET_OK; | ||
1588 | } | ||
1589 | pi.status = GNUNET_FS_STATUS_PUBLISH_RESUME; | ||
1590 | pi.value.publish.specifics.resume.message = fi->emsg; | ||
1591 | pi.value.publish.specifics.resume.chk_uri = fi->chk_uri; | ||
1592 | *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0); | ||
1593 | if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) | ||
1594 | { | ||
1595 | /* process entries in directory */ | ||
1596 | pc->skip_next_fi_callback = GNUNET_YES; | ||
1597 | GNUNET_FS_file_information_inspect (fi, &fip_signal_resume, pc); | ||
1598 | } | ||
1599 | return GNUNET_OK; | ||
1600 | } | ||
1601 | |||
1602 | |||
1603 | /** | ||
1604 | * Function called with a filename of serialized publishing operation | ||
1605 | * to deserialize. | ||
1606 | * | ||
1607 | * @param cls the `struct GNUNET_FS_Handle *` | ||
1608 | * @param filename complete filename (absolute path) | ||
1609 | * @return #GNUNET_OK (continue to iterate) | ||
1610 | */ | ||
1611 | static int | ||
1612 | deserialize_publish_file (void *cls, const char *filename) | ||
1613 | { | ||
1614 | struct GNUNET_FS_Handle *h = cls; | ||
1615 | struct GNUNET_BIO_ReadHandle *rh; | ||
1616 | struct GNUNET_FS_PublishContext *pc; | ||
1617 | int32_t options; | ||
1618 | int32_t all_done; | ||
1619 | int32_t have_ns; | ||
1620 | char *fi_root; | ||
1621 | struct GNUNET_CRYPTO_EcdsaPrivateKey ns; | ||
1622 | char *fi_pos; | ||
1623 | char *emsg; | ||
1624 | |||
1625 | pc = GNUNET_new (struct GNUNET_FS_PublishContext); | ||
1626 | pc->h = h; | ||
1627 | pc->serialization = get_serialization_short_name (filename); | ||
1628 | fi_root = NULL; | ||
1629 | fi_pos = NULL; | ||
1630 | rh = GNUNET_BIO_read_open_file (filename); | ||
1631 | if (NULL == rh) | ||
1632 | { | ||
1633 | GNUNET_break (0); | ||
1634 | goto cleanup; | ||
1635 | } | ||
1636 | struct GNUNET_BIO_ReadSpec rs[] = { | ||
1637 | GNUNET_BIO_read_spec_string ("publish-nid", &pc->nid, 1024), | ||
1638 | GNUNET_BIO_read_spec_string ("publish-nuid", &pc->nuid, 1024), | ||
1639 | GNUNET_BIO_read_spec_int32 ("options", &options), | ||
1640 | GNUNET_BIO_read_spec_int32 ("all done", &all_done), | ||
1641 | GNUNET_BIO_read_spec_int32 ("have ns", &have_ns), | ||
1642 | GNUNET_BIO_read_spec_string ("publish-firoot", &fi_root, 128), | ||
1643 | GNUNET_BIO_read_spec_string ("publish-fipos", &fi_pos, 128), | ||
1644 | GNUNET_BIO_read_spec_end (), | ||
1645 | }; | ||
1646 | if ((GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) || | ||
1647 | ((GNUNET_YES == have_ns) && | ||
1648 | (GNUNET_OK != GNUNET_BIO_read (rh, "publish-ns", &ns, sizeof(ns))))) | ||
1649 | { | ||
1650 | GNUNET_break (0); | ||
1651 | goto cleanup; | ||
1652 | } | ||
1653 | pc->options = options; | ||
1654 | pc->all_done = all_done; | ||
1655 | if (NULL == fi_root) | ||
1656 | { | ||
1657 | GNUNET_break (0); | ||
1658 | goto cleanup; | ||
1659 | } | ||
1660 | pc->fi = deserialize_file_information (h, fi_root); | ||
1661 | if (NULL == pc->fi) | ||
1662 | { | ||
1663 | GNUNET_break (0); | ||
1664 | goto cleanup; | ||
1665 | } | ||
1666 | if (GNUNET_YES == have_ns) | ||
1667 | { | ||
1668 | pc->ns = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey); | ||
1669 | *pc->ns = ns; | ||
1670 | } | ||
1671 | if ((0 == (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) && | ||
1672 | (GNUNET_YES != pc->all_done)) | ||
1673 | { | ||
1674 | pc->dsh = GNUNET_DATASTORE_connect (h->cfg); | ||
1675 | if (NULL == pc->dsh) | ||
1676 | goto cleanup; | ||
1677 | } | ||
1678 | if (NULL != fi_pos) | ||
1679 | { | ||
1680 | pc->fi_pos = find_file_position (pc->fi, fi_pos); | ||
1681 | GNUNET_free (fi_pos); | ||
1682 | fi_pos = NULL; | ||
1683 | if (NULL == pc->fi_pos) | ||
1684 | { | ||
1685 | /* failed to find position for resuming, outch! Will start from root! */ | ||
1686 | GNUNET_break (0); | ||
1687 | if (GNUNET_YES != pc->all_done) | ||
1688 | pc->fi_pos = pc->fi; | ||
1689 | } | ||
1690 | } | ||
1691 | GNUNET_free (fi_root); | ||
1692 | fi_root = NULL; | ||
1693 | /* generate RESUME event(s) */ | ||
1694 | GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_resume, pc); | ||
1695 | |||
1696 | /* re-start publishing (if needed)... */ | ||
1697 | if (GNUNET_YES != pc->all_done) | ||
1698 | { | ||
1699 | GNUNET_assert (NULL == pc->upload_task); | ||
1700 | pc->upload_task = | ||
1701 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, | ||
1702 | &GNUNET_FS_publish_main_, | ||
1703 | pc); | ||
1704 | } | ||
1705 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
1706 | { | ||
1707 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1708 | _ ("Failure while resuming publishing operation `%s': %s\n"), | ||
1709 | filename, | ||
1710 | emsg); | ||
1711 | GNUNET_free (emsg); | ||
1712 | } | ||
1713 | pc->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, pc); | ||
1714 | return GNUNET_OK; | ||
1715 | cleanup: | ||
1716 | GNUNET_free (pc->nid); | ||
1717 | GNUNET_free (pc->nuid); | ||
1718 | GNUNET_free (fi_root); | ||
1719 | GNUNET_free (fi_pos); | ||
1720 | if ((NULL != rh) && (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))) | ||
1721 | { | ||
1722 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1723 | _ ("Failed to resume publishing operation `%s': %s\n"), | ||
1724 | filename, | ||
1725 | emsg); | ||
1726 | GNUNET_free (emsg); | ||
1727 | } | ||
1728 | if (NULL != pc->fi) | ||
1729 | GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL); | ||
1730 | if (0 != unlink (filename)) | ||
1731 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
1732 | GNUNET_free (pc->serialization); | ||
1733 | GNUNET_free (pc); | ||
1734 | return GNUNET_OK; | ||
1735 | } | ||
1736 | |||
1737 | |||
1738 | /** | ||
1739 | * Synchronize this publishing struct with its mirror | ||
1740 | * on disk. Note that all internal FS-operations that change | ||
1741 | * publishing structs should already call "sync" internally, | ||
1742 | * so this function is likely not useful for clients. | ||
1743 | * | ||
1744 | * @param pc the struct to sync | ||
1745 | */ | ||
1746 | void | ||
1747 | GNUNET_FS_publish_sync_ (struct GNUNET_FS_PublishContext *pc) | ||
1748 | { | ||
1749 | struct GNUNET_BIO_WriteHandle *wh; | ||
1750 | int32_t have_ns; | ||
1751 | |||
1752 | if (NULL == pc->serialization) | ||
1753 | pc->serialization = | ||
1754 | make_serialization_file_name (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH); | ||
1755 | if (NULL == pc->serialization) | ||
1756 | return; | ||
1757 | if (NULL == pc->fi) | ||
1758 | return; | ||
1759 | if (NULL == pc->fi->serialization) | ||
1760 | { | ||
1761 | GNUNET_break (0); | ||
1762 | return; | ||
1763 | } | ||
1764 | wh = get_write_handle (pc->h, | ||
1765 | GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, | ||
1766 | pc->serialization); | ||
1767 | if (NULL == wh) | ||
1768 | { | ||
1769 | GNUNET_break (0); | ||
1770 | goto cleanup; | ||
1771 | } | ||
1772 | have_ns = (NULL != pc->ns) ? GNUNET_YES : GNUNET_NO; | ||
1773 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
1774 | GNUNET_BIO_write_spec_string ("nid", pc->nid), | ||
1775 | GNUNET_BIO_write_spec_string ("nuid", pc->nuid), | ||
1776 | GNUNET_BIO_write_spec_int32 ("options", (int32_t *) &pc->options), | ||
1777 | GNUNET_BIO_write_spec_int32 ("all done", &pc->all_done), | ||
1778 | GNUNET_BIO_write_spec_int32 ("have ns", &have_ns), | ||
1779 | GNUNET_BIO_write_spec_string ("serialization", pc->fi->serialization), | ||
1780 | GNUNET_BIO_write_spec_string ("pos serialization", (NULL == pc->fi_pos) | ||
1781 | ? NULL | ||
1782 | : pc->fi_pos->serialization), | ||
1783 | GNUNET_BIO_read_spec_end () | ||
1784 | }; | ||
1785 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)) || | ||
1786 | ((NULL != pc->ns) && | ||
1787 | (GNUNET_OK != | ||
1788 | GNUNET_BIO_write (wh, | ||
1789 | "ns", | ||
1790 | pc->ns, | ||
1791 | sizeof(struct GNUNET_CRYPTO_EcdsaPrivateKey))))) | ||
1792 | { | ||
1793 | GNUNET_break (0); | ||
1794 | goto cleanup; | ||
1795 | } | ||
1796 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
1797 | { | ||
1798 | wh = NULL; | ||
1799 | GNUNET_break (0); | ||
1800 | goto cleanup; | ||
1801 | } | ||
1802 | return; | ||
1803 | cleanup: | ||
1804 | if (NULL != wh) | ||
1805 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
1806 | GNUNET_FS_remove_sync_file_ (pc->h, | ||
1807 | GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, | ||
1808 | pc->serialization); | ||
1809 | GNUNET_free (pc->serialization); | ||
1810 | pc->serialization = NULL; | ||
1811 | } | ||
1812 | |||
1813 | |||
1814 | /** | ||
1815 | * Synchronize this unindex struct with its mirror | ||
1816 | * on disk. Note that all internal FS-operations that change | ||
1817 | * publishing structs should already call "sync" internally, | ||
1818 | * so this function is likely not useful for clients. | ||
1819 | * | ||
1820 | * @param uc the struct to sync | ||
1821 | */ | ||
1822 | void | ||
1823 | GNUNET_FS_unindex_sync_ (struct GNUNET_FS_UnindexContext *uc) | ||
1824 | { | ||
1825 | struct GNUNET_BIO_WriteHandle *wh; | ||
1826 | char *uris; | ||
1827 | |||
1828 | if (NULL == uc->serialization) | ||
1829 | uc->serialization = | ||
1830 | make_serialization_file_name (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX); | ||
1831 | if (NULL == uc->serialization) | ||
1832 | return; | ||
1833 | wh = get_write_handle (uc->h, | ||
1834 | GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, | ||
1835 | uc->serialization); | ||
1836 | if (NULL == wh) | ||
1837 | { | ||
1838 | GNUNET_break (0); | ||
1839 | goto cleanup; | ||
1840 | } | ||
1841 | if (NULL != uc->ksk_uri) | ||
1842 | uris = GNUNET_FS_uri_to_string (uc->ksk_uri); | ||
1843 | else | ||
1844 | uris = NULL; | ||
1845 | struct GNUNET_BIO_WriteSpec ws1[] = { | ||
1846 | GNUNET_BIO_write_spec_string ("filename", uc->filename), | ||
1847 | GNUNET_BIO_write_spec_int64 ("file size", (int64_t *) &uc->file_size), | ||
1848 | GNUNET_BIO_write_spec_end (), | ||
1849 | }; | ||
1850 | struct GNUNET_BIO_WriteSpec ws2[] = { | ||
1851 | GNUNET_BIO_write_spec_int32 ("state", (int32_t *) &uc->state), | ||
1852 | GNUNET_BIO_write_spec_object ("hashkey", &uc->chk, | ||
1853 | sizeof (struct ContentHashKey)), | ||
1854 | GNUNET_BIO_write_spec_string ("uris", uris), | ||
1855 | GNUNET_BIO_write_spec_int32 ("ksk offset", (int32_t *) &uc->ksk_offset), | ||
1856 | GNUNET_BIO_write_spec_end (), | ||
1857 | }; | ||
1858 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws1)) || | ||
1859 | (GNUNET_OK != write_start_time (wh, uc->start_time)) || | ||
1860 | (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws2)) || | ||
1861 | ((uc->state == UNINDEX_STATE_FS_NOTIFY) && | ||
1862 | (GNUNET_OK != GNUNET_BIO_write (wh, | ||
1863 | "file id", | ||
1864 | &uc->file_id, | ||
1865 | sizeof(struct GNUNET_HashCode)))) || | ||
1866 | ((uc->state == UNINDEX_STATE_ERROR) && | ||
1867 | (GNUNET_OK != GNUNET_BIO_write_string (wh, "emsg", uc->emsg)))) | ||
1868 | { | ||
1869 | GNUNET_break (0); | ||
1870 | goto cleanup; | ||
1871 | } | ||
1872 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
1873 | { | ||
1874 | wh = NULL; | ||
1875 | GNUNET_break (0); | ||
1876 | goto cleanup; | ||
1877 | } | ||
1878 | return; | ||
1879 | cleanup: | ||
1880 | if (NULL != wh) | ||
1881 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
1882 | GNUNET_FS_remove_sync_file_ (uc->h, | ||
1883 | GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, | ||
1884 | uc->serialization); | ||
1885 | GNUNET_free (uc->serialization); | ||
1886 | uc->serialization = NULL; | ||
1887 | } | ||
1888 | |||
1889 | |||
1890 | /** | ||
1891 | * Serialize a download request. | ||
1892 | * | ||
1893 | * @param wh handle for writing the download request to disk | ||
1894 | * @param dr the the request to write to disk | ||
1895 | * @return #GNUNET_YES on success, #GNUNET_NO on error | ||
1896 | */ | ||
1897 | static int | ||
1898 | write_download_request (struct GNUNET_BIO_WriteHandle *wh, | ||
1899 | struct DownloadRequest *dr) | ||
1900 | { | ||
1901 | unsigned int i; | ||
1902 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
1903 | GNUNET_BIO_write_spec_int32 ("state", (int32_t *) &dr->state), | ||
1904 | GNUNET_BIO_write_spec_int64 ("offset", (int64_t *) &dr->offset), | ||
1905 | GNUNET_BIO_write_spec_int32 ("num children", (int32_t *) &dr->num_children), | ||
1906 | GNUNET_BIO_write_spec_int32 ("depth", (int32_t *) &dr->depth), | ||
1907 | GNUNET_BIO_write_spec_end (), | ||
1908 | }; | ||
1909 | |||
1910 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))) | ||
1911 | return GNUNET_NO; | ||
1912 | if ((BRS_CHK_SET == dr->state) && | ||
1913 | (GNUNET_OK != | ||
1914 | GNUNET_BIO_write (wh, "hashkey", | ||
1915 | &dr->chk, sizeof(struct ContentHashKey)))) | ||
1916 | return GNUNET_NO; | ||
1917 | for (i = 0; i < dr->num_children; i++) | ||
1918 | if (GNUNET_NO == write_download_request (wh, dr->children[i])) | ||
1919 | return GNUNET_NO; | ||
1920 | return GNUNET_YES; | ||
1921 | } | ||
1922 | |||
1923 | |||
1924 | /** | ||
1925 | * Read a download request tree. | ||
1926 | * | ||
1927 | * @param rh cadet to read from | ||
1928 | * @return value the download request read from disk, NULL on error | ||
1929 | */ | ||
1930 | static struct DownloadRequest * | ||
1931 | read_download_request (struct GNUNET_BIO_ReadHandle *rh) | ||
1932 | { | ||
1933 | struct DownloadRequest *dr; | ||
1934 | unsigned int i; | ||
1935 | |||
1936 | dr = GNUNET_new (struct DownloadRequest); | ||
1937 | struct GNUNET_BIO_ReadSpec rs[] = { | ||
1938 | GNUNET_BIO_read_spec_int32 ("state", (int32_t *) &dr->state), | ||
1939 | GNUNET_BIO_read_spec_int64 ("offset", (int64_t *) &dr->offset), | ||
1940 | GNUNET_BIO_read_spec_int32 ("num children", (int32_t *) &dr->num_children), | ||
1941 | GNUNET_BIO_read_spec_end (), | ||
1942 | }; | ||
1943 | if ((GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) || | ||
1944 | (dr->num_children > CHK_PER_INODE) || | ||
1945 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "depth", | ||
1946 | (int32_t *) &dr->depth)) || | ||
1947 | ((0 == dr->depth) && (dr->num_children > 0)) || | ||
1948 | ((dr->depth > 0) && (0 == dr->num_children))) | ||
1949 | { | ||
1950 | GNUNET_break (0); | ||
1951 | dr->num_children = 0; | ||
1952 | goto cleanup; | ||
1953 | } | ||
1954 | if (dr->num_children > 0) | ||
1955 | dr->children = | ||
1956 | GNUNET_malloc (dr->num_children * sizeof(struct DownloadRequest *)); | ||
1957 | switch (dr->state) | ||
1958 | { | ||
1959 | case BRS_INIT: | ||
1960 | case BRS_RECONSTRUCT_DOWN: | ||
1961 | case BRS_RECONSTRUCT_META_UP: | ||
1962 | case BRS_RECONSTRUCT_UP: | ||
1963 | break; | ||
1964 | |||
1965 | case BRS_CHK_SET: | ||
1966 | if (GNUNET_OK != | ||
1967 | GNUNET_BIO_read (rh, "chk", &dr->chk, sizeof(struct ContentHashKey))) | ||
1968 | goto cleanup; | ||
1969 | break; | ||
1970 | |||
1971 | case BRS_DOWNLOAD_DOWN: | ||
1972 | case BRS_DOWNLOAD_UP: | ||
1973 | case BRS_ERROR: | ||
1974 | break; | ||
1975 | |||
1976 | default: | ||
1977 | GNUNET_break (0); | ||
1978 | goto cleanup; | ||
1979 | } | ||
1980 | for (i = 0; i < dr->num_children; i++) | ||
1981 | { | ||
1982 | if (NULL == (dr->children[i] = read_download_request (rh))) | ||
1983 | goto cleanup; | ||
1984 | dr->children[i]->parent = dr; | ||
1985 | } | ||
1986 | return dr; | ||
1987 | cleanup: | ||
1988 | GNUNET_FS_free_download_request_ (dr); | ||
1989 | return NULL; | ||
1990 | } | ||
1991 | |||
1992 | |||
1993 | /** | ||
1994 | * Compute the name of the sync file (or directory) for the given download | ||
1995 | * context. | ||
1996 | * | ||
1997 | * @param dc download context to compute for | ||
1998 | * @param uni unique filename to use, use "" for the directory name | ||
1999 | * @param ext extension to use, use ".dir" for our own subdirectory | ||
2000 | * @return the expanded file name, NULL for none | ||
2001 | */ | ||
2002 | static char * | ||
2003 | get_download_sync_filename (struct GNUNET_FS_DownloadContext *dc, | ||
2004 | const char *uni, | ||
2005 | const char *ext) | ||
2006 | { | ||
2007 | char *par; | ||
2008 | char *epar; | ||
2009 | |||
2010 | if (dc->parent == NULL) | ||
2011 | return get_serialization_file_name (dc->h, | ||
2012 | (dc->search != NULL) | ||
2013 | ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD | ||
2014 | : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD, | ||
2015 | uni); | ||
2016 | if (NULL == dc->parent->serialization) | ||
2017 | return NULL; | ||
2018 | par = get_download_sync_filename (dc->parent, dc->parent->serialization, ""); | ||
2019 | if (NULL == par) | ||
2020 | return NULL; | ||
2021 | GNUNET_asprintf (&epar, "%s.dir%s%s%s", par, DIR_SEPARATOR_STR, uni, ext); | ||
2022 | GNUNET_free (par); | ||
2023 | return epar; | ||
2024 | } | ||
2025 | |||
2026 | |||
2027 | /** | ||
2028 | * Synchronize this download struct with its mirror | ||
2029 | * on disk. Note that all internal FS-operations that change | ||
2030 | * publishing structs should already call "sync" internally, | ||
2031 | * so this function is likely not useful for clients. | ||
2032 | * | ||
2033 | * @param dc the struct to sync | ||
2034 | */ | ||
2035 | void | ||
2036 | GNUNET_FS_download_sync_ (struct GNUNET_FS_DownloadContext *dc) | ||
2037 | { | ||
2038 | struct GNUNET_BIO_WriteHandle *wh; | ||
2039 | char *uris; | ||
2040 | char *fn; | ||
2041 | char *dir; | ||
2042 | |||
2043 | if (0 != (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE)) | ||
2044 | return; /* we don't sync probes */ | ||
2045 | if (NULL == dc->serialization) | ||
2046 | { | ||
2047 | dir = get_download_sync_filename (dc, "", ""); | ||
2048 | if (NULL == dir) | ||
2049 | return; | ||
2050 | if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dir)) | ||
2051 | { | ||
2052 | GNUNET_free (dir); | ||
2053 | return; | ||
2054 | } | ||
2055 | fn = GNUNET_DISK_mktemp (dir); | ||
2056 | GNUNET_free (dir); | ||
2057 | if (NULL == fn) | ||
2058 | return; | ||
2059 | dc->serialization = get_serialization_short_name (fn); | ||
2060 | } | ||
2061 | else | ||
2062 | { | ||
2063 | fn = get_download_sync_filename (dc, dc->serialization, ""); | ||
2064 | if (NULL == fn) | ||
2065 | { | ||
2066 | GNUNET_free (dc->serialization); | ||
2067 | dc->serialization = NULL; | ||
2068 | GNUNET_free (fn); | ||
2069 | return; | ||
2070 | } | ||
2071 | } | ||
2072 | wh = GNUNET_BIO_write_open_file (fn); | ||
2073 | if (NULL == wh) | ||
2074 | { | ||
2075 | GNUNET_free (dc->serialization); | ||
2076 | dc->serialization = NULL; | ||
2077 | GNUNET_free (fn); | ||
2078 | return; | ||
2079 | } | ||
2080 | GNUNET_assert ((GNUNET_YES == GNUNET_FS_uri_test_chk (dc->uri)) || | ||
2081 | (GNUNET_YES == GNUNET_FS_uri_test_loc (dc->uri))); | ||
2082 | uris = GNUNET_FS_uri_to_string (dc->uri); | ||
2083 | struct GNUNET_BIO_WriteSpec ws1[] = { | ||
2084 | GNUNET_BIO_write_spec_string ("uris", uris), | ||
2085 | GNUNET_FS_write_spec_meta_data ("metadata", dc->meta), | ||
2086 | GNUNET_BIO_write_spec_string ("emsg", dc->emsg), | ||
2087 | GNUNET_BIO_write_spec_string ("filename", dc->filename), | ||
2088 | GNUNET_BIO_write_spec_string ("temp filename", dc->temp_filename), | ||
2089 | GNUNET_BIO_write_spec_int64 ("old file size", | ||
2090 | (int64_t *) &dc->old_file_size), | ||
2091 | GNUNET_BIO_write_spec_int64 ("offset", (int64_t *) &dc->offset), | ||
2092 | GNUNET_BIO_write_spec_int64 ("length", (int64_t *) &dc->length), | ||
2093 | GNUNET_BIO_write_spec_int64 ("completed", (int64_t *) &dc->completed), | ||
2094 | GNUNET_BIO_write_spec_end (), | ||
2095 | }; | ||
2096 | struct GNUNET_BIO_WriteSpec ws2[] = { | ||
2097 | GNUNET_BIO_write_spec_int32 ("anonymity", (int32_t *) &dc->anonymity), | ||
2098 | GNUNET_BIO_write_spec_int32 ("options", (int32_t *) &dc->options), | ||
2099 | GNUNET_BIO_write_spec_int32 ("has finished", (int32_t *) &dc->has_finished), | ||
2100 | GNUNET_BIO_write_spec_end (), | ||
2101 | }; | ||
2102 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws1)) || | ||
2103 | (GNUNET_OK != write_start_time (wh, dc->start_time)) || | ||
2104 | (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws2))) | ||
2105 | { | ||
2106 | GNUNET_break (0); | ||
2107 | goto cleanup; | ||
2108 | } | ||
2109 | if (NULL == dc->emsg) | ||
2110 | { | ||
2111 | GNUNET_assert (dc->top_request != NULL); | ||
2112 | if (GNUNET_YES != write_download_request (wh, dc->top_request)) | ||
2113 | { | ||
2114 | GNUNET_break (0); | ||
2115 | goto cleanup; | ||
2116 | } | ||
2117 | } | ||
2118 | GNUNET_free (uris); | ||
2119 | uris = NULL; | ||
2120 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
2121 | { | ||
2122 | wh = NULL; | ||
2123 | GNUNET_break (0); | ||
2124 | goto cleanup; | ||
2125 | } | ||
2126 | GNUNET_free (fn); | ||
2127 | return; | ||
2128 | cleanup: | ||
2129 | if (NULL != wh) | ||
2130 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
2131 | GNUNET_free (uris); | ||
2132 | if (0 != unlink (fn)) | ||
2133 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); | ||
2134 | GNUNET_free (fn); | ||
2135 | GNUNET_free (dc->serialization); | ||
2136 | dc->serialization = NULL; | ||
2137 | } | ||
2138 | |||
2139 | |||
2140 | /** | ||
2141 | * Synchronize this search result with its mirror | ||
2142 | * on disk. Note that all internal FS-operations that change | ||
2143 | * publishing structs should already call "sync" internally, | ||
2144 | * so this function is likely not useful for clients. | ||
2145 | * | ||
2146 | * @param sr the struct to sync | ||
2147 | */ | ||
2148 | void | ||
2149 | GNUNET_FS_search_result_sync_ (struct GNUNET_FS_SearchResult *sr) | ||
2150 | { | ||
2151 | struct GNUNET_BIO_WriteHandle *wh; | ||
2152 | char *uris; | ||
2153 | |||
2154 | if (NULL == sr->sc) | ||
2155 | return; | ||
2156 | uris = NULL; | ||
2157 | if (NULL == sr->serialization) | ||
2158 | sr->serialization = | ||
2159 | make_serialization_file_name_in_dir (sr->h, | ||
2160 | (sr->sc->psearch_result == NULL) | ||
2161 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2162 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2163 | sr->sc->serialization); | ||
2164 | if (NULL == sr->serialization) | ||
2165 | return; | ||
2166 | wh = get_write_handle_in_dir (sr->h, | ||
2167 | (sr->sc->psearch_result == NULL) | ||
2168 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2169 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2170 | sr->sc->serialization, | ||
2171 | sr->serialization); | ||
2172 | if (NULL == wh) | ||
2173 | { | ||
2174 | GNUNET_break (0); | ||
2175 | goto cleanup; | ||
2176 | } | ||
2177 | uris = GNUNET_FS_uri_to_string (sr->uri); | ||
2178 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
2179 | GNUNET_BIO_write_spec_string ("uris", uris), | ||
2180 | GNUNET_BIO_write_spec_string ("download serialization", | ||
2181 | (sr->download != NULL) | ||
2182 | ? sr->download->serialization | ||
2183 | : NULL), | ||
2184 | GNUNET_BIO_write_spec_string ("update search serialization", | ||
2185 | (sr->update_search != NULL) | ||
2186 | ? sr->update_search->serialization | ||
2187 | : NULL), | ||
2188 | GNUNET_FS_write_spec_meta_data ("metadata", sr->meta), | ||
2189 | GNUNET_BIO_write_spec_object ("key", &sr->key, | ||
2190 | sizeof(struct GNUNET_HashCode)), | ||
2191 | GNUNET_BIO_write_spec_int32 ("mandatory missing", | ||
2192 | (int32_t *) &sr->mandatory_missing), | ||
2193 | GNUNET_BIO_write_spec_int32 ("optional support", | ||
2194 | (int32_t *) &sr->optional_support), | ||
2195 | GNUNET_BIO_write_spec_int32 ("availability success", | ||
2196 | (int32_t *) &sr->availability_success), | ||
2197 | GNUNET_BIO_write_spec_int32 ("availability trials", | ||
2198 | (int32_t *) &sr->availability_trials), | ||
2199 | GNUNET_BIO_write_spec_end (), | ||
2200 | }; | ||
2201 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))) | ||
2202 | { | ||
2203 | GNUNET_break (0); | ||
2204 | goto cleanup; | ||
2205 | } | ||
2206 | if ((NULL != sr->uri) && (GNUNET_FS_URI_KSK == sr->sc->uri->type) && | ||
2207 | (GNUNET_OK != | ||
2208 | GNUNET_BIO_write (wh, | ||
2209 | "keyword bitmap", | ||
2210 | sr->keyword_bitmap, | ||
2211 | (sr->sc->uri->data.ksk.keywordCount + 7) / 8))) | ||
2212 | { | ||
2213 | GNUNET_break (0); | ||
2214 | goto cleanup; | ||
2215 | } | ||
2216 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
2217 | { | ||
2218 | wh = NULL; | ||
2219 | GNUNET_break (0); | ||
2220 | goto cleanup; | ||
2221 | } | ||
2222 | GNUNET_free (uris); | ||
2223 | return; | ||
2224 | cleanup: | ||
2225 | GNUNET_free (uris); | ||
2226 | if (NULL != wh) | ||
2227 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
2228 | remove_sync_file_in_dir (sr->h, | ||
2229 | (NULL == sr->sc->psearch_result) | ||
2230 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2231 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2232 | sr->sc->serialization, | ||
2233 | sr->serialization); | ||
2234 | GNUNET_free (sr->serialization); | ||
2235 | sr->serialization = NULL; | ||
2236 | } | ||
2237 | |||
2238 | |||
2239 | /** | ||
2240 | * Synchronize this search struct with its mirror | ||
2241 | * on disk. Note that all internal FS-operations that change | ||
2242 | * publishing structs should already call "sync" internally, | ||
2243 | * so this function is likely not useful for clients. | ||
2244 | * | ||
2245 | * @param sc the struct to sync | ||
2246 | */ | ||
2247 | void | ||
2248 | GNUNET_FS_search_sync_ (struct GNUNET_FS_SearchContext *sc) | ||
2249 | { | ||
2250 | struct GNUNET_BIO_WriteHandle *wh; | ||
2251 | char *uris; | ||
2252 | char in_pause; | ||
2253 | const char *category; | ||
2254 | |||
2255 | category = (NULL == sc->psearch_result) ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2256 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH; | ||
2257 | if (NULL == sc->serialization) | ||
2258 | sc->serialization = make_serialization_file_name (sc->h, category); | ||
2259 | if (NULL == sc->serialization) | ||
2260 | return; | ||
2261 | uris = NULL; | ||
2262 | wh = get_write_handle (sc->h, category, sc->serialization); | ||
2263 | if (NULL == wh) | ||
2264 | { | ||
2265 | GNUNET_break (0); | ||
2266 | goto cleanup; | ||
2267 | } | ||
2268 | GNUNET_assert ((GNUNET_YES == GNUNET_FS_uri_test_ksk (sc->uri)) || | ||
2269 | (GNUNET_YES == GNUNET_FS_uri_test_sks (sc->uri))); | ||
2270 | uris = GNUNET_FS_uri_to_string (sc->uri); | ||
2271 | in_pause = (sc->task != NULL) ? 'r' : '\0'; | ||
2272 | if ((GNUNET_OK != GNUNET_BIO_write_string (wh, "uris", uris)) || | ||
2273 | (GNUNET_OK != write_start_time (wh, sc->start_time)) || | ||
2274 | (GNUNET_OK != GNUNET_BIO_write_string (wh, "emsg", sc->emsg)) || | ||
2275 | (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "options", | ||
2276 | (uint32_t) sc->options)) || | ||
2277 | (GNUNET_OK != GNUNET_BIO_write (wh, "in pause", | ||
2278 | &in_pause, sizeof(in_pause))) || | ||
2279 | (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "anonymity", sc->anonymity))) | ||
2280 | { | ||
2281 | GNUNET_break (0); | ||
2282 | goto cleanup; | ||
2283 | } | ||
2284 | GNUNET_free (uris); | ||
2285 | uris = NULL; | ||
2286 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
2287 | { | ||
2288 | wh = NULL; | ||
2289 | GNUNET_break (0); | ||
2290 | goto cleanup; | ||
2291 | } | ||
2292 | return; | ||
2293 | cleanup: | ||
2294 | if (NULL != wh) | ||
2295 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
2296 | GNUNET_free (uris); | ||
2297 | GNUNET_FS_remove_sync_file_ (sc->h, category, sc->serialization); | ||
2298 | GNUNET_free (sc->serialization); | ||
2299 | sc->serialization = NULL; | ||
2300 | } | ||
2301 | |||
2302 | |||
2303 | /** | ||
2304 | * Function called with a filename of serialized unindexing operation | ||
2305 | * to deserialize. | ||
2306 | * | ||
2307 | * @param cls the `struct GNUNET_FS_Handle *` | ||
2308 | * @param filename complete filename (absolute path) | ||
2309 | * @return #GNUNET_OK (continue to iterate) | ||
2310 | */ | ||
2311 | static int | ||
2312 | deserialize_unindex_file (void *cls, const char *filename) | ||
2313 | { | ||
2314 | struct GNUNET_FS_Handle *h = cls; | ||
2315 | struct GNUNET_BIO_ReadHandle *rh; | ||
2316 | struct GNUNET_FS_UnindexContext *uc; | ||
2317 | struct GNUNET_FS_ProgressInfo pi; | ||
2318 | char *emsg; | ||
2319 | char *uris; | ||
2320 | uint32_t state; | ||
2321 | |||
2322 | uc = GNUNET_new (struct GNUNET_FS_UnindexContext); | ||
2323 | uc->h = h; | ||
2324 | uc->serialization = get_serialization_short_name (filename); | ||
2325 | rh = GNUNET_BIO_read_open_file (filename); | ||
2326 | if (NULL == rh) | ||
2327 | { | ||
2328 | GNUNET_break (0); | ||
2329 | goto cleanup; | ||
2330 | } | ||
2331 | uris = NULL; | ||
2332 | if ((GNUNET_OK != | ||
2333 | GNUNET_BIO_read_string (rh, "unindex-fn", &uc->filename, 10 * 1024)) || | ||
2334 | (GNUNET_OK != GNUNET_BIO_read_int64 (rh, "file size", | ||
2335 | (int64_t *) &uc->file_size)) || | ||
2336 | (GNUNET_OK != read_start_time (rh, &uc->start_time)) || | ||
2337 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "state", | ||
2338 | (int32_t *) &state)) || | ||
2339 | (GNUNET_OK != | ||
2340 | GNUNET_BIO_read (rh, "uri", &uc->chk, sizeof(struct ContentHashKey))) || | ||
2341 | (GNUNET_OK != | ||
2342 | GNUNET_BIO_read_string (rh, "unindex-kskuri", &uris, 10 * 1024)) || | ||
2343 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "ksk offset", | ||
2344 | (int32_t *) &uc->ksk_offset))) | ||
2345 | { | ||
2346 | GNUNET_free (uris); | ||
2347 | GNUNET_break (0); | ||
2348 | goto cleanup; | ||
2349 | } | ||
2350 | if (NULL != uris) | ||
2351 | { | ||
2352 | uc->ksk_uri = GNUNET_FS_uri_parse (uris, &emsg); | ||
2353 | GNUNET_free (uris); | ||
2354 | if (NULL == uc->ksk_uri) | ||
2355 | { | ||
2356 | GNUNET_break (0); | ||
2357 | GNUNET_free (emsg); | ||
2358 | goto cleanup; | ||
2359 | } | ||
2360 | } | ||
2361 | if ((uc->ksk_offset > 0) && | ||
2362 | ((NULL == uc->ksk_uri) || | ||
2363 | (uc->ksk_offset > uc->ksk_uri->data.ksk.keywordCount))) | ||
2364 | { | ||
2365 | GNUNET_break (0); | ||
2366 | goto cleanup; | ||
2367 | } | ||
2368 | uc->state = (enum UnindexState) state; | ||
2369 | switch (state) | ||
2370 | { | ||
2371 | case UNINDEX_STATE_HASHING: | ||
2372 | break; | ||
2373 | |||
2374 | case UNINDEX_STATE_FS_NOTIFY: | ||
2375 | if (GNUNET_OK != GNUNET_BIO_read (rh, | ||
2376 | "unindex-hash", | ||
2377 | &uc->file_id, | ||
2378 | sizeof(struct GNUNET_HashCode))) | ||
2379 | { | ||
2380 | GNUNET_break (0); | ||
2381 | goto cleanup; | ||
2382 | } | ||
2383 | break; | ||
2384 | |||
2385 | case UNINDEX_STATE_DS_REMOVE: | ||
2386 | case UNINDEX_STATE_EXTRACT_KEYWORDS: | ||
2387 | case UNINDEX_STATE_DS_REMOVE_KBLOCKS: | ||
2388 | break; | ||
2389 | |||
2390 | case UNINDEX_STATE_COMPLETE: | ||
2391 | break; | ||
2392 | |||
2393 | case UNINDEX_STATE_ERROR: | ||
2394 | if (GNUNET_OK != | ||
2395 | GNUNET_BIO_read_string (rh, "unindex-emsg", &uc->emsg, 10 * 1024)) | ||
2396 | { | ||
2397 | GNUNET_break (0); | ||
2398 | goto cleanup; | ||
2399 | } | ||
2400 | break; | ||
2401 | |||
2402 | default: | ||
2403 | GNUNET_break (0); | ||
2404 | goto cleanup; | ||
2405 | } | ||
2406 | uc->top = GNUNET_FS_make_top (h, &GNUNET_FS_unindex_signal_suspend_, uc); | ||
2407 | pi.status = GNUNET_FS_STATUS_UNINDEX_RESUME; | ||
2408 | pi.value.unindex.specifics.resume.message = uc->emsg; | ||
2409 | GNUNET_FS_unindex_make_status_ (&pi, | ||
2410 | uc, | ||
2411 | (uc->state == UNINDEX_STATE_COMPLETE) | ||
2412 | ? uc->file_size | ||
2413 | : 0); | ||
2414 | switch (uc->state) | ||
2415 | { | ||
2416 | case UNINDEX_STATE_HASHING: | ||
2417 | uc->fhc = GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
2418 | uc->filename, | ||
2419 | HASHING_BLOCKSIZE, | ||
2420 | &GNUNET_FS_unindex_process_hash_, | ||
2421 | uc); | ||
2422 | break; | ||
2423 | |||
2424 | case UNINDEX_STATE_FS_NOTIFY: | ||
2425 | uc->state = UNINDEX_STATE_HASHING; | ||
2426 | GNUNET_FS_unindex_process_hash_ (uc, &uc->file_id); | ||
2427 | break; | ||
2428 | |||
2429 | case UNINDEX_STATE_DS_REMOVE: | ||
2430 | GNUNET_FS_unindex_do_remove_ (uc); | ||
2431 | break; | ||
2432 | |||
2433 | case UNINDEX_STATE_EXTRACT_KEYWORDS: | ||
2434 | GNUNET_FS_unindex_do_extract_keywords_ (uc); | ||
2435 | break; | ||
2436 | |||
2437 | case UNINDEX_STATE_DS_REMOVE_KBLOCKS: | ||
2438 | GNUNET_FS_unindex_do_remove_kblocks_ (uc); | ||
2439 | break; | ||
2440 | |||
2441 | case UNINDEX_STATE_COMPLETE: | ||
2442 | case UNINDEX_STATE_ERROR: | ||
2443 | /* no need to resume any operation, we were done */ | ||
2444 | break; | ||
2445 | |||
2446 | default: | ||
2447 | break; | ||
2448 | } | ||
2449 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
2450 | { | ||
2451 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2452 | _ ("Failure while resuming unindexing operation `%s': %s\n"), | ||
2453 | filename, | ||
2454 | emsg); | ||
2455 | GNUNET_free (emsg); | ||
2456 | } | ||
2457 | return GNUNET_OK; | ||
2458 | cleanup: | ||
2459 | GNUNET_free (uc->filename); | ||
2460 | if ((NULL != rh) && (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))) | ||
2461 | { | ||
2462 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2463 | _ ("Failed to resume unindexing operation `%s': %s\n"), | ||
2464 | filename, | ||
2465 | emsg); | ||
2466 | GNUNET_free (emsg); | ||
2467 | } | ||
2468 | if (NULL != uc->serialization) | ||
2469 | GNUNET_FS_remove_sync_file_ (h, | ||
2470 | GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, | ||
2471 | uc->serialization); | ||
2472 | GNUNET_free (uc->serialization); | ||
2473 | GNUNET_free (uc); | ||
2474 | return GNUNET_OK; | ||
2475 | } | ||
2476 | |||
2477 | |||
2478 | /** | ||
2479 | * Deserialize a download. | ||
2480 | * | ||
2481 | * @param h overall context | ||
2482 | * @param rh file to deserialize from | ||
2483 | * @param parent parent download | ||
2484 | * @param search associated search | ||
2485 | * @param serialization name under which the search was serialized | ||
2486 | */ | ||
2487 | static void | ||
2488 | deserialize_download (struct GNUNET_FS_Handle *h, | ||
2489 | struct GNUNET_BIO_ReadHandle *rh, | ||
2490 | struct GNUNET_FS_DownloadContext *parent, | ||
2491 | struct GNUNET_FS_SearchResult *search, | ||
2492 | const char *serialization); | ||
2493 | |||
2494 | |||
2495 | /** | ||
2496 | * Deserialize a search. | ||
2497 | * | ||
2498 | * @param h overall context | ||
2499 | * @param rh file to deserialize from | ||
2500 | * @param psearch_result parent search result | ||
2501 | * @param serialization name under which the search was serialized | ||
2502 | */ | ||
2503 | static struct GNUNET_FS_SearchContext * | ||
2504 | deserialize_search (struct GNUNET_FS_Handle *h, | ||
2505 | struct GNUNET_BIO_ReadHandle *rh, | ||
2506 | struct GNUNET_FS_SearchResult *psearch_result, | ||
2507 | const char *serialization); | ||
2508 | |||
2509 | |||
2510 | /** | ||
2511 | * Function called with a filename of serialized search result | ||
2512 | * to deserialize. | ||
2513 | * | ||
2514 | * @param cls the `struct GNUNET_FS_SearchContext *` | ||
2515 | * @param filename complete filename (absolute path) | ||
2516 | * @return #GNUNET_OK (continue to iterate) | ||
2517 | */ | ||
2518 | static int | ||
2519 | deserialize_search_result (void *cls, const char *filename) | ||
2520 | { | ||
2521 | struct GNUNET_FS_SearchContext *sc = cls; | ||
2522 | char *serialized; | ||
2523 | char *uris; | ||
2524 | char *emsg; | ||
2525 | char *download; | ||
2526 | char *update_srch; | ||
2527 | struct GNUNET_BIO_ReadHandle *rh; | ||
2528 | struct GNUNET_BIO_ReadHandle *drh; | ||
2529 | struct GNUNET_FS_SearchResult *sr; | ||
2530 | |||
2531 | serialized = get_serialization_short_name (filename); | ||
2532 | rh = GNUNET_BIO_read_open_file (filename); | ||
2533 | if (NULL == rh) | ||
2534 | { | ||
2535 | if (NULL != serialized) | ||
2536 | { | ||
2537 | remove_sync_file_in_dir (sc->h, | ||
2538 | (NULL == sc->psearch_result) | ||
2539 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2540 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2541 | sc->serialization, | ||
2542 | serialized); | ||
2543 | GNUNET_free (serialized); | ||
2544 | } | ||
2545 | return GNUNET_OK; | ||
2546 | } | ||
2547 | emsg = NULL; | ||
2548 | uris = NULL; | ||
2549 | download = NULL; | ||
2550 | update_srch = NULL; | ||
2551 | sr = GNUNET_new (struct GNUNET_FS_SearchResult); | ||
2552 | sr->h = sc->h; | ||
2553 | sr->sc = sc; | ||
2554 | sr->serialization = serialized; | ||
2555 | if ((GNUNET_OK != | ||
2556 | GNUNET_BIO_read_string (rh, "result-uri", &uris, 10 * 1024)) || | ||
2557 | (NULL == (sr->uri = GNUNET_FS_uri_parse (uris, &emsg))) || | ||
2558 | (GNUNET_OK != | ||
2559 | GNUNET_BIO_read_string (rh, "download-lnk", &download, 16)) || | ||
2560 | (GNUNET_OK != | ||
2561 | GNUNET_BIO_read_string (rh, "search-lnk", &update_srch, 16)) || | ||
2562 | (GNUNET_OK != GNUNET_FS_read_meta_data (rh, "result-meta", &sr->meta)) || | ||
2563 | (GNUNET_OK != GNUNET_BIO_read (rh, | ||
2564 | "result-key", | ||
2565 | &sr->key, | ||
2566 | sizeof(struct GNUNET_HashCode))) || | ||
2567 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
2568 | rh, | ||
2569 | "mandatory missing", | ||
2570 | (int32_t *) &sr->mandatory_missing)) || | ||
2571 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
2572 | rh, | ||
2573 | "optional support", | ||
2574 | (int32_t *) &sr->optional_support)) || | ||
2575 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
2576 | rh, | ||
2577 | "availability success", | ||
2578 | (int32_t *) &sr->availability_success)) || | ||
2579 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
2580 | rh, | ||
2581 | "availability trials", | ||
2582 | (int32_t *) &sr->availability_trials))) | ||
2583 | { | ||
2584 | GNUNET_break (0); | ||
2585 | goto cleanup; | ||
2586 | } | ||
2587 | if (GNUNET_FS_URI_KSK == sr->sc->uri->type) | ||
2588 | { | ||
2589 | sr->keyword_bitmap = GNUNET_malloc ( | ||
2590 | (sr->sc->uri->data.ksk.keywordCount + 7) / 8); /* round up, count bits */ | ||
2591 | if (GNUNET_OK != | ||
2592 | GNUNET_BIO_read (rh, | ||
2593 | "keyword-bitmap", | ||
2594 | sr->keyword_bitmap, | ||
2595 | (sr->sc->uri->data.ksk.keywordCount + 7) / 8)) | ||
2596 | { | ||
2597 | GNUNET_break (0); | ||
2598 | goto cleanup; | ||
2599 | } | ||
2600 | } | ||
2601 | GNUNET_free (uris); | ||
2602 | if (NULL != download) | ||
2603 | { | ||
2604 | drh = get_read_handle (sc->h, GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD, download); | ||
2605 | if (NULL != drh) | ||
2606 | { | ||
2607 | deserialize_download (sc->h, drh, NULL, sr, download); | ||
2608 | if (GNUNET_OK != GNUNET_BIO_read_close (drh, &emsg)) | ||
2609 | { | ||
2610 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2611 | _ ("Failed to resume sub-download `%s': %s\n"), | ||
2612 | download, | ||
2613 | emsg); | ||
2614 | GNUNET_free (emsg); | ||
2615 | } | ||
2616 | } | ||
2617 | GNUNET_free (download); | ||
2618 | } | ||
2619 | if (NULL != update_srch) | ||
2620 | { | ||
2621 | drh = | ||
2622 | get_read_handle (sc->h, GNUNET_FS_SYNC_PATH_CHILD_SEARCH, update_srch); | ||
2623 | if (NULL != drh) | ||
2624 | { | ||
2625 | deserialize_search (sc->h, drh, sr, update_srch); | ||
2626 | if (GNUNET_OK != GNUNET_BIO_read_close (drh, &emsg)) | ||
2627 | { | ||
2628 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2629 | _ ("Failed to resume sub-search `%s': %s\n"), | ||
2630 | update_srch, | ||
2631 | emsg); | ||
2632 | GNUNET_free (emsg); | ||
2633 | } | ||
2634 | } | ||
2635 | GNUNET_free (update_srch); | ||
2636 | } | ||
2637 | GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multihashmap_put ( | ||
2638 | sc->master_result_map, | ||
2639 | &sr->key, | ||
2640 | sr, | ||
2641 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); | ||
2642 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
2643 | { | ||
2644 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2645 | _ ("Failure while resuming search operation `%s': %s\n"), | ||
2646 | filename, | ||
2647 | emsg); | ||
2648 | GNUNET_free (emsg); | ||
2649 | } | ||
2650 | return GNUNET_OK; | ||
2651 | cleanup: | ||
2652 | GNUNET_free (download); | ||
2653 | GNUNET_free (emsg); | ||
2654 | GNUNET_free (uris); | ||
2655 | GNUNET_free (update_srch); | ||
2656 | if (NULL != sr->uri) | ||
2657 | GNUNET_FS_uri_destroy (sr->uri); | ||
2658 | if (NULL != sr->meta) | ||
2659 | GNUNET_FS_meta_data_destroy (sr->meta); | ||
2660 | GNUNET_free (sr->serialization); | ||
2661 | GNUNET_free (sr); | ||
2662 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
2663 | { | ||
2664 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2665 | _ ("Failure while resuming search operation `%s': %s\n"), | ||
2666 | filename, | ||
2667 | emsg); | ||
2668 | GNUNET_free (emsg); | ||
2669 | } | ||
2670 | return GNUNET_OK; | ||
2671 | } | ||
2672 | |||
2673 | |||
2674 | /** | ||
2675 | * Send the 'resume' signal to the callback; also actually | ||
2676 | * resume the download (put it in the queue). Does this | ||
2677 | * recursively for the top-level download and all child | ||
2678 | * downloads. | ||
2679 | * | ||
2680 | * @param dc download to resume | ||
2681 | */ | ||
2682 | static void | ||
2683 | signal_download_resume (struct GNUNET_FS_DownloadContext *dc) | ||
2684 | { | ||
2685 | struct GNUNET_FS_DownloadContext *dcc; | ||
2686 | struct GNUNET_FS_ProgressInfo pi; | ||
2687 | |||
2688 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_RESUME; | ||
2689 | pi.value.download.specifics.resume.meta = dc->meta; | ||
2690 | pi.value.download.specifics.resume.message = dc->emsg; | ||
2691 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
2692 | dcc = dc->child_head; | ||
2693 | while (NULL != dcc) | ||
2694 | { | ||
2695 | signal_download_resume (dcc); | ||
2696 | dcc = dcc->next; | ||
2697 | } | ||
2698 | } | ||
2699 | |||
2700 | |||
2701 | /** | ||
2702 | * Signal resuming of a search to our clients (for the | ||
2703 | * top level search and all sub-searches). | ||
2704 | * | ||
2705 | * @param sc search being resumed | ||
2706 | */ | ||
2707 | static void | ||
2708 | signal_search_resume (struct GNUNET_FS_SearchContext *sc); | ||
2709 | |||
2710 | |||
2711 | /** | ||
2712 | * Iterator over search results signaling resume to the client for | ||
2713 | * each result. | ||
2714 | * | ||
2715 | * @param cls closure, the `struct GNUNET_FS_SearchContext *` | ||
2716 | * @param key current key code | ||
2717 | * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *` | ||
2718 | * @return #GNUNET_YES (we should continue to iterate) | ||
2719 | */ | ||
2720 | static int | ||
2721 | signal_result_resume (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
2722 | { | ||
2723 | struct GNUNET_FS_SearchContext *sc = cls; | ||
2724 | struct GNUNET_FS_ProgressInfo pi; | ||
2725 | struct GNUNET_FS_SearchResult *sr = value; | ||
2726 | |||
2727 | if (0 == sr->mandatory_missing) | ||
2728 | { | ||
2729 | pi.status = GNUNET_FS_STATUS_SEARCH_RESUME_RESULT; | ||
2730 | pi.value.search.specifics.resume_result.meta = sr->meta; | ||
2731 | pi.value.search.specifics.resume_result.uri = sr->uri; | ||
2732 | pi.value.search.specifics.resume_result.result = sr; | ||
2733 | pi.value.search.specifics.resume_result.availability_rank = | ||
2734 | 2 * sr->availability_success - sr->availability_trials; | ||
2735 | pi.value.search.specifics.resume_result.availability_certainty = | ||
2736 | sr->availability_trials; | ||
2737 | pi.value.search.specifics.resume_result.applicability_rank = | ||
2738 | sr->optional_support; | ||
2739 | sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
2740 | } | ||
2741 | if (NULL != sr->download) | ||
2742 | { | ||
2743 | signal_download_resume (sr->download); | ||
2744 | } | ||
2745 | else | ||
2746 | { | ||
2747 | GNUNET_FS_search_start_probe_ (sr); | ||
2748 | } | ||
2749 | if (NULL != sr->update_search) | ||
2750 | signal_search_resume (sr->update_search); | ||
2751 | return GNUNET_YES; | ||
2752 | } | ||
2753 | |||
2754 | |||
2755 | /** | ||
2756 | * Free memory allocated by the search context and its children | ||
2757 | * | ||
2758 | * @param sc search context to free | ||
2759 | */ | ||
2760 | static void | ||
2761 | free_search_context (struct GNUNET_FS_SearchContext *sc); | ||
2762 | |||
2763 | |||
2764 | /** | ||
2765 | * Iterator over search results freeing each. | ||
2766 | * | ||
2767 | * @param cls closure, the `struct GNUNET_FS_SearchContext *` | ||
2768 | * @param key current key code | ||
2769 | * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *` | ||
2770 | * @return #GNUNET_YES (we should continue to iterate) | ||
2771 | */ | ||
2772 | static int | ||
2773 | free_result (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
2774 | { | ||
2775 | struct GNUNET_FS_SearchResult *sr = value; | ||
2776 | |||
2777 | if (NULL != sr->update_search) | ||
2778 | { | ||
2779 | free_search_context (sr->update_search); | ||
2780 | GNUNET_assert (NULL == sr->update_search); | ||
2781 | } | ||
2782 | GNUNET_FS_meta_data_destroy (sr->meta); | ||
2783 | GNUNET_FS_uri_destroy (sr->uri); | ||
2784 | GNUNET_free (sr); | ||
2785 | return GNUNET_YES; | ||
2786 | } | ||
2787 | |||
2788 | |||
2789 | /** | ||
2790 | * Free memory allocated by the search context and its children | ||
2791 | * | ||
2792 | * @param sc search context to free | ||
2793 | */ | ||
2794 | static void | ||
2795 | free_search_context (struct GNUNET_FS_SearchContext *sc) | ||
2796 | { | ||
2797 | if (NULL != sc->serialization) | ||
2798 | { | ||
2799 | GNUNET_FS_remove_sync_file_ (sc->h, | ||
2800 | (sc->psearch_result == NULL) | ||
2801 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2802 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2803 | sc->serialization); | ||
2804 | GNUNET_FS_remove_sync_dir_ (sc->h, | ||
2805 | (sc->psearch_result == NULL) | ||
2806 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2807 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2808 | sc->serialization); | ||
2809 | } | ||
2810 | GNUNET_free (sc->serialization); | ||
2811 | GNUNET_free (sc->emsg); | ||
2812 | if (NULL != sc->uri) | ||
2813 | GNUNET_FS_uri_destroy (sc->uri); | ||
2814 | if (NULL != sc->master_result_map) | ||
2815 | { | ||
2816 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
2817 | &free_result, | ||
2818 | sc); | ||
2819 | GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map); | ||
2820 | } | ||
2821 | GNUNET_free (sc); | ||
2822 | } | ||
2823 | |||
2824 | |||
2825 | /** | ||
2826 | * Function called with a filename of serialized sub-download | ||
2827 | * to deserialize. | ||
2828 | * | ||
2829 | * @param cls the `struct GNUNET_FS_DownloadContext *` (parent) | ||
2830 | * @param filename complete filename (absolute path) | ||
2831 | * @return #GNUNET_OK (continue to iterate) | ||
2832 | */ | ||
2833 | static int | ||
2834 | deserialize_subdownload (void *cls, const char *filename) | ||
2835 | { | ||
2836 | struct GNUNET_FS_DownloadContext *parent = cls; | ||
2837 | char *serialized; | ||
2838 | char *emsg; | ||
2839 | struct GNUNET_BIO_ReadHandle *rh; | ||
2840 | |||
2841 | serialized = get_serialization_short_name (filename); | ||
2842 | rh = GNUNET_BIO_read_open_file (filename); | ||
2843 | if (NULL == rh) | ||
2844 | { | ||
2845 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2846 | _ ( | ||
2847 | "Failed to resume sub-download `%s': could not open file `%s'\n"), | ||
2848 | serialized, | ||
2849 | filename); | ||
2850 | GNUNET_free (serialized); | ||
2851 | return GNUNET_OK; | ||
2852 | } | ||
2853 | deserialize_download (parent->h, rh, parent, NULL, serialized); | ||
2854 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
2855 | { | ||
2856 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2857 | _ ("Failed to resume sub-download `%s': %s\n"), | ||
2858 | serialized, | ||
2859 | emsg); | ||
2860 | GNUNET_free (emsg); | ||
2861 | } | ||
2862 | GNUNET_free (serialized); | ||
2863 | return GNUNET_OK; | ||
2864 | } | ||
2865 | |||
2866 | |||
2867 | /** | ||
2868 | * Free this download context and all of its descendants. | ||
2869 | * (only works during deserialization since not all possible | ||
2870 | * state it taken care of). | ||
2871 | * | ||
2872 | * @param dc context to free | ||
2873 | */ | ||
2874 | static void | ||
2875 | free_download_context (struct GNUNET_FS_DownloadContext *dc) | ||
2876 | { | ||
2877 | struct GNUNET_FS_DownloadContext *dcc; | ||
2878 | |||
2879 | if (NULL != dc->meta) | ||
2880 | GNUNET_FS_meta_data_destroy (dc->meta); | ||
2881 | if (NULL != dc->uri) | ||
2882 | GNUNET_FS_uri_destroy (dc->uri); | ||
2883 | GNUNET_free (dc->temp_filename); | ||
2884 | GNUNET_free (dc->emsg); | ||
2885 | GNUNET_free (dc->filename); | ||
2886 | GNUNET_free (dc->serialization); | ||
2887 | while (NULL != (dcc = dc->child_head)) | ||
2888 | { | ||
2889 | GNUNET_CONTAINER_DLL_remove (dc->child_head, dc->child_tail, dcc); | ||
2890 | free_download_context (dcc); | ||
2891 | } | ||
2892 | GNUNET_FS_free_download_request_ (dc->top_request); | ||
2893 | if (NULL != dc->active) | ||
2894 | GNUNET_CONTAINER_multihashmap_destroy (dc->active); | ||
2895 | GNUNET_free (dc); | ||
2896 | } | ||
2897 | |||
2898 | |||
2899 | /** | ||
2900 | * Deserialize a download. | ||
2901 | * | ||
2902 | * @param h overall context | ||
2903 | * @param rh file to deserialize from | ||
2904 | * @param parent parent download | ||
2905 | * @param search associated search | ||
2906 | * @param serialization name under which the search was serialized | ||
2907 | */ | ||
2908 | static void | ||
2909 | deserialize_download (struct GNUNET_FS_Handle *h, | ||
2910 | struct GNUNET_BIO_ReadHandle *rh, | ||
2911 | struct GNUNET_FS_DownloadContext *parent, | ||
2912 | struct GNUNET_FS_SearchResult *search, | ||
2913 | const char *serialization) | ||
2914 | { | ||
2915 | struct GNUNET_FS_DownloadContext *dc; | ||
2916 | char *emsg; | ||
2917 | char *uris; | ||
2918 | char *dn; | ||
2919 | uint32_t options; | ||
2920 | uint32_t status; | ||
2921 | |||
2922 | uris = NULL; | ||
2923 | emsg = NULL; | ||
2924 | dc = GNUNET_new (struct GNUNET_FS_DownloadContext); | ||
2925 | dc->parent = parent; | ||
2926 | dc->h = h; | ||
2927 | dc->serialization = GNUNET_strdup (serialization); | ||
2928 | struct GNUNET_BIO_ReadSpec rs[] = { | ||
2929 | GNUNET_FS_read_spec_meta_data ("download-meta", &dc->meta), | ||
2930 | GNUNET_BIO_read_spec_string ("download-emsg", &dc->emsg, 10 * 1024), | ||
2931 | GNUNET_BIO_read_spec_string ("download-fn", &dc->filename, 10 * 1024), | ||
2932 | GNUNET_BIO_read_spec_string ("download-tfn", | ||
2933 | &dc->temp_filename, 10 * 1024), | ||
2934 | GNUNET_BIO_read_spec_int64 ("old file size", | ||
2935 | (int64_t *) &dc->old_file_size), | ||
2936 | GNUNET_BIO_read_spec_int64 ("offset", | ||
2937 | (int64_t *) &dc->offset), | ||
2938 | GNUNET_BIO_read_spec_int64 ("length", | ||
2939 | (int64_t *) &dc->length), | ||
2940 | GNUNET_BIO_read_spec_int64 ("completed", | ||
2941 | (int64_t *) &dc->completed), | ||
2942 | GNUNET_BIO_read_spec_end (), | ||
2943 | }; | ||
2944 | if ((GNUNET_OK != | ||
2945 | GNUNET_BIO_read_string (rh, "download-uri", &uris, 10 * 1024)) || | ||
2946 | (NULL == (dc->uri = GNUNET_FS_uri_parse (uris, &emsg))) || | ||
2947 | ((GNUNET_YES != GNUNET_FS_uri_test_chk (dc->uri)) && | ||
2948 | (GNUNET_YES != GNUNET_FS_uri_test_loc (dc->uri))) || | ||
2949 | (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) || | ||
2950 | (GNUNET_OK != read_start_time (rh, &dc->start_time)) || | ||
2951 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "anonymity", | ||
2952 | (int32_t *) &dc->anonymity)) || | ||
2953 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "options", | ||
2954 | (int32_t *) &options)) || | ||
2955 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "status", | ||
2956 | (int32_t *) &status))) | ||
2957 | { | ||
2958 | GNUNET_break (0); | ||
2959 | goto cleanup; | ||
2960 | } | ||
2961 | dc->options = (enum GNUNET_FS_DownloadOptions) options; | ||
2962 | dc->active = | ||
2963 | GNUNET_CONTAINER_multihashmap_create (1 + 2 * (dc->length / DBLOCK_SIZE), | ||
2964 | GNUNET_NO); | ||
2965 | dc->has_finished = (int) status; | ||
2966 | dc->treedepth = | ||
2967 | GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri)); | ||
2968 | if (GNUNET_FS_uri_test_loc (dc->uri)) | ||
2969 | GNUNET_assert (GNUNET_OK == | ||
2970 | GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target)); | ||
2971 | if (NULL == dc->emsg) | ||
2972 | { | ||
2973 | dc->top_request = read_download_request (rh); | ||
2974 | if (NULL == dc->top_request) | ||
2975 | { | ||
2976 | GNUNET_break (0); | ||
2977 | goto cleanup; | ||
2978 | } | ||
2979 | } | ||
2980 | dn = get_download_sync_filename (dc, dc->serialization, ".dir"); | ||
2981 | if (NULL != dn) | ||
2982 | { | ||
2983 | if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES)) | ||
2984 | GNUNET_DISK_directory_scan (dn, &deserialize_subdownload, dc); | ||
2985 | GNUNET_free (dn); | ||
2986 | } | ||
2987 | if (NULL != parent) | ||
2988 | { | ||
2989 | GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc); | ||
2990 | } | ||
2991 | if (NULL != search) | ||
2992 | { | ||
2993 | dc->search = search; | ||
2994 | search->download = dc; | ||
2995 | } | ||
2996 | if ((NULL == parent) && (NULL == search)) | ||
2997 | { | ||
2998 | dc->top = | ||
2999 | GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc); | ||
3000 | signal_download_resume (dc); | ||
3001 | } | ||
3002 | GNUNET_free (uris); | ||
3003 | GNUNET_assert (NULL == dc->job_queue); | ||
3004 | dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc); | ||
3005 | return; | ||
3006 | cleanup: | ||
3007 | GNUNET_free (uris); | ||
3008 | GNUNET_free (emsg); | ||
3009 | free_download_context (dc); | ||
3010 | } | ||
3011 | |||
3012 | |||
3013 | /** | ||
3014 | * Signal resuming of a search to our clients (for the | ||
3015 | * top level search and all sub-searches). | ||
3016 | * | ||
3017 | * @param sc search being resumed | ||
3018 | */ | ||
3019 | static void | ||
3020 | signal_search_resume (struct GNUNET_FS_SearchContext *sc) | ||
3021 | { | ||
3022 | struct GNUNET_FS_ProgressInfo pi; | ||
3023 | |||
3024 | pi.status = GNUNET_FS_STATUS_SEARCH_RESUME; | ||
3025 | pi.value.search.specifics.resume.message = sc->emsg; | ||
3026 | pi.value.search.specifics.resume.is_paused = | ||
3027 | (NULL == sc->mq) ? GNUNET_YES : GNUNET_NO; | ||
3028 | sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
3029 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
3030 | &signal_result_resume, | ||
3031 | sc); | ||
3032 | } | ||
3033 | |||
3034 | |||
3035 | /** | ||
3036 | * Deserialize a search. | ||
3037 | * | ||
3038 | * @param h overall context | ||
3039 | * @param rh file to deserialize from | ||
3040 | * @param psearch_result parent search result | ||
3041 | * @param serialization name under which the search was serialized | ||
3042 | */ | ||
3043 | static struct GNUNET_FS_SearchContext * | ||
3044 | deserialize_search (struct GNUNET_FS_Handle *h, | ||
3045 | struct GNUNET_BIO_ReadHandle *rh, | ||
3046 | struct GNUNET_FS_SearchResult *psearch_result, | ||
3047 | const char *serialization) | ||
3048 | { | ||
3049 | struct GNUNET_FS_SearchContext *sc; | ||
3050 | char *emsg; | ||
3051 | char *uris; | ||
3052 | char *dn; | ||
3053 | uint32_t options; | ||
3054 | char in_pause; | ||
3055 | |||
3056 | if ((NULL != psearch_result) && (NULL != psearch_result->update_search)) | ||
3057 | { | ||
3058 | GNUNET_break (0); | ||
3059 | return NULL; | ||
3060 | } | ||
3061 | uris = NULL; | ||
3062 | emsg = NULL; | ||
3063 | sc = GNUNET_new (struct GNUNET_FS_SearchContext); | ||
3064 | if (NULL != psearch_result) | ||
3065 | { | ||
3066 | sc->psearch_result = psearch_result; | ||
3067 | psearch_result->update_search = sc; | ||
3068 | } | ||
3069 | sc->h = h; | ||
3070 | sc->serialization = GNUNET_strdup (serialization); | ||
3071 | if ((GNUNET_OK != | ||
3072 | GNUNET_BIO_read_string (rh, "search-uri", &uris, 10 * 1024)) || | ||
3073 | (NULL == (sc->uri = GNUNET_FS_uri_parse (uris, &emsg))) || | ||
3074 | ((GNUNET_YES != GNUNET_FS_uri_test_ksk (sc->uri)) && | ||
3075 | (GNUNET_YES != GNUNET_FS_uri_test_sks (sc->uri))) || | ||
3076 | (GNUNET_OK != read_start_time (rh, &sc->start_time)) || | ||
3077 | (GNUNET_OK != | ||
3078 | GNUNET_BIO_read_string (rh, "search-emsg", &sc->emsg, 10 * 1024)) || | ||
3079 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "options", | ||
3080 | (int32_t *) &options)) || | ||
3081 | (GNUNET_OK != | ||
3082 | GNUNET_BIO_read (rh, "search-pause", &in_pause, sizeof(in_pause))) || | ||
3083 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "anonymity", | ||
3084 | (int32_t *) &sc->anonymity))) | ||
3085 | { | ||
3086 | GNUNET_break (0); | ||
3087 | goto cleanup; | ||
3088 | } | ||
3089 | sc->options = (enum GNUNET_FS_SearchOptions) options; | ||
3090 | sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO); | ||
3091 | dn = get_serialization_file_name_in_dir (h, | ||
3092 | (NULL == sc->psearch_result) | ||
3093 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
3094 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
3095 | sc->serialization, | ||
3096 | ""); | ||
3097 | if (NULL != dn) | ||
3098 | { | ||
3099 | if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES)) | ||
3100 | GNUNET_DISK_directory_scan (dn, &deserialize_search_result, sc); | ||
3101 | GNUNET_free (dn); | ||
3102 | } | ||
3103 | if (('\0' == in_pause) && | ||
3104 | (GNUNET_OK != GNUNET_FS_search_start_searching_ (sc))) | ||
3105 | { | ||
3106 | GNUNET_log ( | ||
3107 | GNUNET_ERROR_TYPE_WARNING, | ||
3108 | _ ("Could not resume running search, will resume as paused search\n")); | ||
3109 | } | ||
3110 | signal_search_resume (sc); | ||
3111 | GNUNET_free (uris); | ||
3112 | return sc; | ||
3113 | cleanup: | ||
3114 | GNUNET_free (emsg); | ||
3115 | free_search_context (sc); | ||
3116 | GNUNET_free (uris); | ||
3117 | return NULL; | ||
3118 | } | ||
3119 | |||
3120 | |||
3121 | /** | ||
3122 | * Function called with a filename of serialized search operation | ||
3123 | * to deserialize. | ||
3124 | * | ||
3125 | * @param cls the `struct GNUNET_FS_Handle *` | ||
3126 | * @param filename complete filename (absolute path) | ||
3127 | * @return #GNUNET_OK (continue to iterate) | ||
3128 | */ | ||
3129 | static int | ||
3130 | deserialize_search_file (void *cls, const char *filename) | ||
3131 | { | ||
3132 | struct GNUNET_FS_Handle *h = cls; | ||
3133 | char *set; | ||
3134 | char *emsg; | ||
3135 | struct GNUNET_BIO_ReadHandle *rh; | ||
3136 | struct GNUNET_FS_SearchContext *sc; | ||
3137 | struct stat buf; | ||
3138 | |||
3139 | if (0 != stat (filename, &buf)) | ||
3140 | { | ||
3141 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename); | ||
3142 | return GNUNET_OK; | ||
3143 | } | ||
3144 | if (S_ISDIR (buf.st_mode)) | ||
3145 | return GNUNET_OK; /* skip directories */ | ||
3146 | set = get_serialization_short_name (filename); | ||
3147 | rh = GNUNET_BIO_read_open_file (filename); | ||
3148 | if (NULL == rh) | ||
3149 | { | ||
3150 | if (NULL != set) | ||
3151 | { | ||
3152 | GNUNET_FS_remove_sync_file_ (h, GNUNET_FS_SYNC_PATH_MASTER_SEARCH, set); | ||
3153 | GNUNET_free (set); | ||
3154 | } | ||
3155 | return GNUNET_OK; | ||
3156 | } | ||
3157 | sc = deserialize_search (h, rh, NULL, set); | ||
3158 | if (NULL != sc) | ||
3159 | sc->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, sc); | ||
3160 | GNUNET_free (set); | ||
3161 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
3162 | { | ||
3163 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
3164 | _ ("Failure while resuming search operation `%s': %s\n"), | ||
3165 | filename, | ||
3166 | emsg); | ||
3167 | GNUNET_free (emsg); | ||
3168 | } | ||
3169 | return GNUNET_OK; | ||
3170 | } | ||
3171 | |||
3172 | |||
3173 | /** | ||
3174 | * Function called with a filename of serialized download operation | ||
3175 | * to deserialize. | ||
3176 | * | ||
3177 | * @param cls the `struct GNUNET_FS_Handle *` | ||
3178 | * @param filename complete filename (absolute path) | ||
3179 | * @return #GNUNET_OK (continue to iterate) | ||
3180 | */ | ||
3181 | static int | ||
3182 | deserialize_download_file (void *cls, const char *filename) | ||
3183 | { | ||
3184 | struct GNUNET_FS_Handle *h = cls; | ||
3185 | char *set; | ||
3186 | char *emsg; | ||
3187 | struct GNUNET_BIO_ReadHandle *rh; | ||
3188 | |||
3189 | set = get_serialization_short_name (filename); | ||
3190 | rh = GNUNET_BIO_read_open_file (filename); | ||
3191 | if (NULL == rh) | ||
3192 | { | ||
3193 | if (0 != unlink (filename)) | ||
3194 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
3195 | "unlink", | ||
3196 | filename); | ||
3197 | GNUNET_free (set); | ||
3198 | return GNUNET_OK; | ||
3199 | } | ||
3200 | deserialize_download (h, rh, NULL, NULL, set); | ||
3201 | GNUNET_free (set); | ||
3202 | if (GNUNET_OK != | ||
3203 | GNUNET_BIO_read_close (rh, | ||
3204 | &emsg)) | ||
3205 | { | ||
3206 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
3207 | "Failure while resuming download operation `%s': %s\n", | ||
3208 | filename, | ||
3209 | emsg); | ||
3210 | GNUNET_free (emsg); | ||
3211 | } | ||
3212 | return GNUNET_OK; | ||
3213 | } | ||
3214 | |||
3215 | |||
3216 | /** | ||
3217 | * Deserialize information about pending operations. | ||
3218 | * | ||
3219 | * @param master_path which master directory should be scanned | ||
3220 | * @param proc function to call for each entry (will get @a h for 'cls') | ||
3221 | * @param h the `struct GNUNET_FS_Handle *` | ||
3222 | */ | ||
3223 | static void | ||
3224 | deserialization_master (const char *master_path, | ||
3225 | GNUNET_FileNameCallback proc, | ||
3226 | struct GNUNET_FS_Handle *h) | ||
3227 | { | ||
3228 | char *dn; | ||
3229 | |||
3230 | dn = get_serialization_file_name (h, master_path, ""); | ||
3231 | if (NULL == dn) | ||
3232 | return; | ||
3233 | if (GNUNET_YES == | ||
3234 | GNUNET_DISK_directory_test (dn, | ||
3235 | GNUNET_YES)) | ||
3236 | GNUNET_DISK_directory_scan (dn, | ||
3237 | proc, | ||
3238 | h); | ||
3239 | GNUNET_free (dn); | ||
3240 | } | ||
3241 | |||
3242 | |||
3243 | struct GNUNET_FS_Handle * | ||
3244 | GNUNET_FS_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
3245 | const char *client_name, | ||
3246 | GNUNET_FS_ProgressCallback upcb, | ||
3247 | void *upcb_cls, | ||
3248 | enum GNUNET_FS_Flags flags, | ||
3249 | ...) | ||
3250 | { | ||
3251 | struct GNUNET_FS_Handle *ret; | ||
3252 | enum GNUNET_FS_OPTIONS opt; | ||
3253 | va_list ap; | ||
3254 | |||
3255 | ret = GNUNET_new (struct GNUNET_FS_Handle); | ||
3256 | ret->cfg = cfg; | ||
3257 | ret->client_name = GNUNET_strdup (client_name); | ||
3258 | ret->upcb = upcb; | ||
3259 | ret->upcb_cls = upcb_cls; | ||
3260 | ret->flags = flags; | ||
3261 | ret->max_parallel_downloads = DEFAULT_MAX_PARALLEL_DOWNLOADS; | ||
3262 | ret->max_parallel_requests = DEFAULT_MAX_PARALLEL_REQUESTS; | ||
3263 | ret->avg_block_latency = | ||
3264 | GNUNET_TIME_UNIT_MINUTES; /* conservative starting point */ | ||
3265 | va_start (ap, flags); | ||
3266 | while (GNUNET_FS_OPTIONS_END != | ||
3267 | (opt = GNUNET_VA_ARG_ENUM (ap, GNUNET_FS_OPTIONS))) | ||
3268 | { | ||
3269 | switch (opt) | ||
3270 | { | ||
3271 | case GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM: | ||
3272 | ret->max_parallel_downloads = va_arg (ap, unsigned int); | ||
3273 | |||
3274 | break; | ||
3275 | |||
3276 | case GNUNET_FS_OPTIONS_REQUEST_PARALLELISM: | ||
3277 | ret->max_parallel_requests = va_arg (ap, unsigned int); | ||
3278 | |||
3279 | break; | ||
3280 | |||
3281 | default: | ||
3282 | GNUNET_break (0); | ||
3283 | GNUNET_free (ret->client_name); | ||
3284 | GNUNET_free (ret); | ||
3285 | va_end (ap); | ||
3286 | return NULL; | ||
3287 | } | ||
3288 | } | ||
3289 | va_end (ap); | ||
3290 | if (0 != (GNUNET_FS_FLAGS_PERSISTENCE & flags)) | ||
3291 | { | ||
3292 | deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, | ||
3293 | &deserialize_publish_file, | ||
3294 | ret); | ||
3295 | deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_SEARCH, | ||
3296 | &deserialize_search_file, | ||
3297 | ret); | ||
3298 | deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD, | ||
3299 | &deserialize_download_file, | ||
3300 | ret); | ||
3301 | deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, | ||
3302 | &deserialize_unindex_file, | ||
3303 | ret); | ||
3304 | } | ||
3305 | return ret; | ||
3306 | } | ||
3307 | |||
3308 | |||
3309 | void | ||
3310 | GNUNET_FS_stop (struct GNUNET_FS_Handle *h) | ||
3311 | { | ||
3312 | while (NULL != h->top_head) | ||
3313 | h->top_head->ssf (h->top_head->ssf_cls); | ||
3314 | if (NULL != h->queue_job) | ||
3315 | GNUNET_SCHEDULER_cancel (h->queue_job); | ||
3316 | GNUNET_free (h->client_name); | ||
3317 | GNUNET_free (h); | ||
3318 | } | ||
3319 | |||
3320 | |||
3321 | /* end of fs_api.c */ | ||
diff --git a/src/fs/fs_api.h b/src/fs/fs_api.h deleted file mode 100644 index fdda91928..000000000 --- a/src/fs/fs_api.h +++ /dev/null | |||
@@ -1,1941 +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 | |||
33 | #include "gnunet_fs_service.h" | ||
34 | #include "gnunet_block_lib.h" | ||
35 | #include "block_fs.h" | ||
36 | #include "fs.h" | ||
37 | |||
38 | /** | ||
39 | * Pick a multiple of 2 here to achieve 8-byte alignment! We also | ||
40 | * probably want DBlocks to have (roughly) the same size as IBlocks. | ||
41 | * With SHA-512, the optimal value is 32768 byte / 128 byte = 256 (128 | ||
42 | * byte = 2 * 512 bits). DO NOT CHANGE! | ||
43 | */ | ||
44 | #define CHK_PER_INODE 256 | ||
45 | |||
46 | /** | ||
47 | * Maximum size for a file to be considered for inlining in a | ||
48 | * directory. | ||
49 | */ | ||
50 | #define MAX_INLINE_SIZE 65536 | ||
51 | |||
52 | /** | ||
53 | * Name of the directory with top-level searches. | ||
54 | */ | ||
55 | #define GNUNET_FS_SYNC_PATH_MASTER_SEARCH "search" | ||
56 | |||
57 | /** | ||
58 | * Name of the directory with sub-searches (namespace-updates). | ||
59 | */ | ||
60 | #define GNUNET_FS_SYNC_PATH_CHILD_SEARCH "search-child" | ||
61 | |||
62 | /** | ||
63 | * Name of the directory with master downloads (not associated | ||
64 | * with search or part of another download). | ||
65 | */ | ||
66 | #define GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD "download" | ||
67 | |||
68 | /** | ||
69 | * Name of the directory with downloads that are part of another | ||
70 | * download or a search. | ||
71 | */ | ||
72 | #define GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD "download-child" | ||
73 | |||
74 | /** | ||
75 | * Name of the directory with publishing operations. | ||
76 | */ | ||
77 | #define GNUNET_FS_SYNC_PATH_MASTER_PUBLISH "publish" | ||
78 | |||
79 | /** | ||
80 | * Name of the directory with files that are being published | ||
81 | */ | ||
82 | #define GNUNET_FS_SYNC_PATH_FILE_INFO "publish-file" | ||
83 | |||
84 | /** | ||
85 | * Name of the directory with unindex operations. | ||
86 | */ | ||
87 | #define GNUNET_FS_SYNC_PATH_MASTER_UNINDEX "unindex" | ||
88 | |||
89 | |||
90 | /** | ||
91 | * @brief complete information needed | ||
92 | * to download a file. | ||
93 | */ | ||
94 | struct FileIdentifier | ||
95 | { | ||
96 | /** | ||
97 | * Total size of the file in bytes. (network byte order (!)) | ||
98 | */ | ||
99 | uint64_t file_length; | ||
100 | |||
101 | /** | ||
102 | * Query and key of the top GNUNET_EC_IBlock. | ||
103 | */ | ||
104 | struct ContentHashKey chk; | ||
105 | }; | ||
106 | |||
107 | |||
108 | /** | ||
109 | * Information about a file and its location | ||
110 | * (peer claiming to share the file). | ||
111 | */ | ||
112 | struct Location | ||
113 | { | ||
114 | /** | ||
115 | * Information about the shared file. | ||
116 | */ | ||
117 | struct FileIdentifier fi; | ||
118 | |||
119 | /** | ||
120 | * Identity of the peer sharing the file. | ||
121 | */ | ||
122 | struct GNUNET_PeerIdentity peer; | ||
123 | |||
124 | /** | ||
125 | * Time when this location URI expires. | ||
126 | */ | ||
127 | struct GNUNET_TIME_Absolute expirationTime; | ||
128 | |||
129 | /** | ||
130 | * Signature over the GNUNET_EC_FileIdentifier, | ||
131 | * peer identity and expiration time. | ||
132 | */ | ||
133 | struct GNUNET_CRYPTO_EddsaSignature contentSignature; | ||
134 | }; | ||
135 | |||
136 | /** | ||
137 | * Types of URIs. | ||
138 | */ | ||
139 | enum GNUNET_FS_UriType | ||
140 | { | ||
141 | /** | ||
142 | * Content-hash-key (simple file). | ||
143 | */ | ||
144 | GNUNET_FS_URI_CHK, | ||
145 | |||
146 | /** | ||
147 | * Signed key space (file in namespace). | ||
148 | */ | ||
149 | GNUNET_FS_URI_SKS, | ||
150 | |||
151 | /** | ||
152 | * Keyword search key (query with keywords). | ||
153 | */ | ||
154 | GNUNET_FS_URI_KSK, | ||
155 | |||
156 | /** | ||
157 | * Location (chk with identity of hosting peer). | ||
158 | */ | ||
159 | GNUNET_FS_URI_LOC | ||
160 | }; | ||
161 | |||
162 | |||
163 | /** | ||
164 | * A Universal Resource Identifier (URI), opaque. | ||
165 | */ | ||
166 | struct GNUNET_FS_Uri | ||
167 | { | ||
168 | /** | ||
169 | * Type of the URI. | ||
170 | */ | ||
171 | enum GNUNET_FS_UriType type; | ||
172 | |||
173 | union | ||
174 | { | ||
175 | struct | ||
176 | { | ||
177 | /** | ||
178 | * Keywords start with a '+' if they are mandatory (in which | ||
179 | * case the '+' is NOT part of the keyword) and with a simple | ||
180 | * space if they are optional (in which case the space is ALSO | ||
181 | * not part of the actual keyword). | ||
182 | * | ||
183 | * Double-quotes to protect spaces and %-encoding are NOT used | ||
184 | * internally (only in URI-strings). | ||
185 | */ | ||
186 | char **keywords; | ||
187 | |||
188 | /** | ||
189 | * Size of the keywords array. | ||
190 | */ | ||
191 | unsigned int keywordCount; | ||
192 | } ksk; | ||
193 | |||
194 | struct | ||
195 | { | ||
196 | /** | ||
197 | * Identifier of the namespace. | ||
198 | */ | ||
199 | struct GNUNET_CRYPTO_EcdsaPublicKey ns; | ||
200 | |||
201 | /** | ||
202 | * Human-readable identifier chosen for this entry in the | ||
203 | * namespace. | ||
204 | */ | ||
205 | char *identifier; | ||
206 | } sks; | ||
207 | |||
208 | /** | ||
209 | * Information needed to retrieve a file (content-hash-key | ||
210 | * plus file size). | ||
211 | */ | ||
212 | struct FileIdentifier chk; | ||
213 | |||
214 | /** | ||
215 | * Information needed to retrieve a file including signed | ||
216 | * location (identity of a peer) of the content. | ||
217 | */ | ||
218 | struct Location loc; | ||
219 | } data; | ||
220 | }; | ||
221 | |||
222 | |||
223 | /** | ||
224 | * Information for a file or directory that is | ||
225 | * about to be published. | ||
226 | */ | ||
227 | struct GNUNET_FS_FileInformation | ||
228 | { | ||
229 | /** | ||
230 | * Files in a directory are kept as a linked list. | ||
231 | */ | ||
232 | struct GNUNET_FS_FileInformation *next; | ||
233 | |||
234 | /** | ||
235 | * If this is a file in a directory, "dir" refers to | ||
236 | * the directory; otherwise NULL. | ||
237 | */ | ||
238 | struct GNUNET_FS_FileInformation *dir; | ||
239 | |||
240 | /** | ||
241 | * Handle to the master context. | ||
242 | */ | ||
243 | struct GNUNET_FS_Handle *h; | ||
244 | |||
245 | /** | ||
246 | * Pointer kept for the client. | ||
247 | */ | ||
248 | void *client_info; | ||
249 | |||
250 | /** | ||
251 | * Metadata to use for the file. | ||
252 | */ | ||
253 | struct GNUNET_FS_MetaData *meta; | ||
254 | |||
255 | /** | ||
256 | * Keywords to use for KBlocks. | ||
257 | */ | ||
258 | struct GNUNET_FS_Uri *keywords; | ||
259 | |||
260 | /** | ||
261 | * CHK for this file or directory. NULL if | ||
262 | * we have not yet computed it. | ||
263 | */ | ||
264 | struct GNUNET_FS_Uri *chk_uri; | ||
265 | |||
266 | /** | ||
267 | * SKS URI for this file or directory. NULL if | ||
268 | * we have not yet computed it. | ||
269 | */ | ||
270 | struct GNUNET_FS_Uri *sks_uri; | ||
271 | |||
272 | /** | ||
273 | * Block options for the file. | ||
274 | */ | ||
275 | struct GNUNET_FS_BlockOptions bo; | ||
276 | |||
277 | /** | ||
278 | * At what time did we start this upload? | ||
279 | */ | ||
280 | struct GNUNET_TIME_Absolute start_time; | ||
281 | |||
282 | /** | ||
283 | * Under what filename is this struct serialized | ||
284 | * (for operational persistence). Should be determined | ||
285 | * using 'mktemp'. | ||
286 | */ | ||
287 | char *serialization; | ||
288 | |||
289 | /** | ||
290 | * Encoder being used to publish this file. | ||
291 | */ | ||
292 | struct GNUNET_FS_TreeEncoder *te; | ||
293 | |||
294 | /** | ||
295 | * Error message (non-NULL if this operation failed). | ||
296 | */ | ||
297 | char *emsg; | ||
298 | |||
299 | /** | ||
300 | * Name of the file or directory (must be an absolute path). | ||
301 | */ | ||
302 | char *filename; | ||
303 | |||
304 | /** | ||
305 | * Data describing either the file or the directory. | ||
306 | */ | ||
307 | union | ||
308 | { | ||
309 | /** | ||
310 | * Data for a file. | ||
311 | */ | ||
312 | struct | ||
313 | { | ||
314 | /** | ||
315 | * Function that can be used to read the data for the file. | ||
316 | */ | ||
317 | GNUNET_FS_DataReader reader; | ||
318 | |||
319 | /** | ||
320 | * Closure for reader. | ||
321 | */ | ||
322 | void *reader_cls; | ||
323 | |||
324 | /** | ||
325 | * If this file is being indexed, this value is set to the hash | ||
326 | * over the entire file (when the indexing process is started). | ||
327 | * Otherwise this field is not used. | ||
328 | */ | ||
329 | struct GNUNET_HashCode file_id; | ||
330 | |||
331 | /** | ||
332 | * Size of the file (in bytes). | ||
333 | */ | ||
334 | uint64_t file_size; | ||
335 | |||
336 | /** | ||
337 | * Should the file be indexed or inserted? | ||
338 | */ | ||
339 | int do_index; | ||
340 | |||
341 | /** | ||
342 | * Is "file_id" already valid? Set to #GNUNET_YES once the hash | ||
343 | * has been calculated. | ||
344 | */ | ||
345 | int have_hash; | ||
346 | |||
347 | /** | ||
348 | * Has the service confirmed our INDEX_START request? | ||
349 | * #GNUNET_YES if this step has been completed. | ||
350 | */ | ||
351 | int index_start_confirmed; | ||
352 | } file; | ||
353 | |||
354 | /** | ||
355 | * Data for a directory. | ||
356 | */ | ||
357 | struct | ||
358 | { | ||
359 | /** | ||
360 | * Linked list of entries in the directory. | ||
361 | */ | ||
362 | struct GNUNET_FS_FileInformation *entries; | ||
363 | |||
364 | /** | ||
365 | * Size of the directory itself (in bytes); 0 if the | ||
366 | * size has not yet been calculated. | ||
367 | */ | ||
368 | size_t dir_size; | ||
369 | |||
370 | /** | ||
371 | * Pointer to the data for the directory (or NULL if not | ||
372 | * available). | ||
373 | */ | ||
374 | void *dir_data; | ||
375 | |||
376 | /** | ||
377 | * How much of the directory have we published (relative to @e contents_size). | ||
378 | */ | ||
379 | uint64_t contents_completed; | ||
380 | |||
381 | /** | ||
382 | * Sum of all of the sizes of all of the files in the directory. | ||
383 | */ | ||
384 | uint64_t contents_size; | ||
385 | } dir; | ||
386 | } data; | ||
387 | |||
388 | /** | ||
389 | * Is this struct for a file or directory? | ||
390 | */ | ||
391 | int is_directory; | ||
392 | |||
393 | /** | ||
394 | * Are we done publishing this file? | ||
395 | */ | ||
396 | int is_published; | ||
397 | }; | ||
398 | |||
399 | |||
400 | /** | ||
401 | * Priorities for the queue. | ||
402 | */ | ||
403 | enum GNUNET_FS_QueuePriority | ||
404 | { | ||
405 | /** | ||
406 | * This is a probe (low priority). | ||
407 | */ | ||
408 | GNUNET_FS_QUEUE_PRIORITY_PROBE, | ||
409 | |||
410 | /** | ||
411 | * Default priority. | ||
412 | */ | ||
413 | GNUNET_FS_QUEUE_PRIORITY_NORMAL | ||
414 | }; | ||
415 | |||
416 | |||
417 | /** | ||
418 | * Entry in the job queue. | ||
419 | */ | ||
420 | struct GNUNET_FS_QueueEntry | ||
421 | { | ||
422 | /** | ||
423 | * This is a linked list. | ||
424 | */ | ||
425 | struct GNUNET_FS_QueueEntry *next; | ||
426 | |||
427 | /** | ||
428 | * This is a linked list. | ||
429 | */ | ||
430 | struct GNUNET_FS_QueueEntry *prev; | ||
431 | |||
432 | /** | ||
433 | * Function to call when the job is started. | ||
434 | */ | ||
435 | GNUNET_SCHEDULER_TaskCallback start; | ||
436 | |||
437 | /** | ||
438 | * Function to call when the job needs to stop (or is done / dequeued). | ||
439 | */ | ||
440 | GNUNET_SCHEDULER_TaskCallback stop; | ||
441 | |||
442 | /** | ||
443 | * Closure for start and stop. | ||
444 | */ | ||
445 | void *cls; | ||
446 | |||
447 | /** | ||
448 | * Handle to FS primary context. | ||
449 | */ | ||
450 | struct GNUNET_FS_Handle *h; | ||
451 | |||
452 | /** | ||
453 | * Message queue handle, or NULL if job is not running. | ||
454 | */ | ||
455 | struct GNUNET_MQ_Handle *mq; | ||
456 | |||
457 | /** | ||
458 | * Time the job was originally queued. | ||
459 | */ | ||
460 | struct GNUNET_TIME_Absolute queue_time; | ||
461 | |||
462 | /** | ||
463 | * Time the job was started last. | ||
464 | */ | ||
465 | struct GNUNET_TIME_Absolute start_time; | ||
466 | |||
467 | /** | ||
468 | * Total amount of time the job has been running (except for the | ||
469 | * current run). | ||
470 | */ | ||
471 | struct GNUNET_TIME_Relative run_time; | ||
472 | |||
473 | /** | ||
474 | * How many blocks do the active downloads have? | ||
475 | */ | ||
476 | unsigned int blocks; | ||
477 | |||
478 | /** | ||
479 | * How important is this download? | ||
480 | */ | ||
481 | enum GNUNET_FS_QueuePriority priority; | ||
482 | |||
483 | /** | ||
484 | * How often have we (re)started this download? | ||
485 | */ | ||
486 | unsigned int start_times; | ||
487 | |||
488 | /** | ||
489 | * #GNUNET_YES if the job is active now. | ||
490 | */ | ||
491 | int active; | ||
492 | }; | ||
493 | |||
494 | |||
495 | /** | ||
496 | * Information we store for each search result. | ||
497 | */ | ||
498 | struct GNUNET_FS_SearchResult | ||
499 | { | ||
500 | /** | ||
501 | * File-sharing context this result belongs to. | ||
502 | */ | ||
503 | struct GNUNET_FS_Handle *h; | ||
504 | |||
505 | /** | ||
506 | * Kept in a DLL while probing. | ||
507 | */ | ||
508 | struct GNUNET_FS_SearchResult *next; | ||
509 | |||
510 | /** | ||
511 | * Kept in a DLL while probing. | ||
512 | */ | ||
513 | struct GNUNET_FS_SearchResult *prev; | ||
514 | |||
515 | /** | ||
516 | * Search context this result belongs to; can be NULL | ||
517 | * for probes that come from a directory result. | ||
518 | */ | ||
519 | struct GNUNET_FS_SearchContext *sc; | ||
520 | |||
521 | /** | ||
522 | * URI to which this search result refers to. | ||
523 | */ | ||
524 | struct GNUNET_FS_Uri *uri; | ||
525 | |||
526 | /** | ||
527 | * Metadata for the search result. | ||
528 | */ | ||
529 | struct GNUNET_FS_MetaData *meta; | ||
530 | |||
531 | /** | ||
532 | * Client info for this search result. | ||
533 | */ | ||
534 | void *client_info; | ||
535 | |||
536 | /** | ||
537 | * ID of a job that is currently probing this results' availability | ||
538 | * (NULL if we are not currently probing). | ||
539 | */ | ||
540 | struct GNUNET_FS_DownloadContext *probe_ctx; | ||
541 | |||
542 | /** | ||
543 | * ID of an associated download based on this search result (or | ||
544 | * NULL for none). | ||
545 | */ | ||
546 | struct GNUNET_FS_DownloadContext *download; | ||
547 | |||
548 | /** | ||
549 | * If this search result triggered an update search, this field | ||
550 | * links to the update search. | ||
551 | */ | ||
552 | struct GNUNET_FS_SearchContext *update_search; | ||
553 | |||
554 | /** | ||
555 | * Name under which this search result is stored on disk. | ||
556 | */ | ||
557 | char *serialization; | ||
558 | |||
559 | /** | ||
560 | * Bitmap that specifies precisely which keywords have been matched already. | ||
561 | */ | ||
562 | uint8_t *keyword_bitmap; | ||
563 | |||
564 | /** | ||
565 | * Key for the search result based on the URI. | ||
566 | */ | ||
567 | struct GNUNET_HashCode key; | ||
568 | |||
569 | /** | ||
570 | * ID of the task that will clean up the probe_ctx should it not | ||
571 | * complete on time (and that will need to be cancelled if we clean | ||
572 | * up the search result before then). | ||
573 | */ | ||
574 | struct GNUNET_SCHEDULER_Task *probe_cancel_task; | ||
575 | |||
576 | /** | ||
577 | * When did the current probe become active? | ||
578 | */ | ||
579 | struct GNUNET_TIME_Absolute probe_active_time; | ||
580 | |||
581 | /** | ||
582 | * How much longer should we run the current probe before giving up? | ||
583 | */ | ||
584 | struct GNUNET_TIME_Relative remaining_probe_time; | ||
585 | |||
586 | /** | ||
587 | * Anonymity level to use for probes using this search result. | ||
588 | */ | ||
589 | uint32_t anonymity; | ||
590 | |||
591 | /** | ||
592 | * Number of mandatory keywords for which we have NOT yet found the | ||
593 | * search result; when this value hits zero, the search result is | ||
594 | * given to the callback. | ||
595 | */ | ||
596 | uint32_t mandatory_missing; | ||
597 | |||
598 | /** | ||
599 | * Number of optional keywords under which this result was also | ||
600 | * found. | ||
601 | */ | ||
602 | uint32_t optional_support; | ||
603 | |||
604 | /** | ||
605 | * Number of availability tests that have succeeded for this result. | ||
606 | */ | ||
607 | uint32_t availability_success; | ||
608 | |||
609 | /** | ||
610 | * Number of availability trials that we have performed for this | ||
611 | * search result. | ||
612 | */ | ||
613 | uint32_t availability_trials; | ||
614 | }; | ||
615 | |||
616 | |||
617 | /** | ||
618 | * Add a job to the queue. | ||
619 | * | ||
620 | * @param h handle to the overall FS state | ||
621 | * @param start function to call to begin the job | ||
622 | * @param stop function to call to pause the job, or on dequeue (if the job was running) | ||
623 | * @param cls closure for start and stop | ||
624 | * @param blocks number of blocks this download has | ||
625 | * @param priority how important is this download | ||
626 | * @return queue handle | ||
627 | */ | ||
628 | struct GNUNET_FS_QueueEntry * | ||
629 | GNUNET_FS_queue_ (struct GNUNET_FS_Handle *h, | ||
630 | GNUNET_SCHEDULER_TaskCallback start, | ||
631 | GNUNET_SCHEDULER_TaskCallback stop, | ||
632 | void *cls, | ||
633 | unsigned int blocks, | ||
634 | enum GNUNET_FS_QueuePriority priority); | ||
635 | |||
636 | |||
637 | /** | ||
638 | * Dequeue a job from the queue. | ||
639 | * | ||
640 | * @param qe handle for the job | ||
641 | */ | ||
642 | void | ||
643 | GNUNET_FS_dequeue_ (struct GNUNET_FS_QueueEntry *qe); | ||
644 | |||
645 | |||
646 | /** | ||
647 | * Function that provides data by reading from a file. | ||
648 | * | ||
649 | * @param cls closure (points to the file information) | ||
650 | * @param offset offset to read from; it is possible | ||
651 | * that the caller might need to go backwards | ||
652 | * a bit at times | ||
653 | * @param max maximum number of bytes that should be | ||
654 | * copied to @a buf; readers are not allowed | ||
655 | * to provide less data unless there is an error; | ||
656 | * a value of "0" will be used at the end to allow | ||
657 | * the reader to clean up its internal state | ||
658 | * @param buf where the reader should write the data | ||
659 | * @param emsg location for the reader to store an error message | ||
660 | * @return number of bytes written, usually "max", 0 on error | ||
661 | */ | ||
662 | size_t | ||
663 | GNUNET_FS_data_reader_file_ (void *cls, | ||
664 | uint64_t offset, | ||
665 | size_t max, | ||
666 | void *buf, | ||
667 | char **emsg); | ||
668 | |||
669 | |||
670 | /** | ||
671 | * Create the closure for the #GNUNET_FS_data_reader_file_() callback. | ||
672 | * | ||
673 | * @param filename file to read | ||
674 | * @return closure to use, NULL on error | ||
675 | */ | ||
676 | void * | ||
677 | GNUNET_FS_make_file_reader_context_ (const char *filename); | ||
678 | |||
679 | |||
680 | /** | ||
681 | * Function that provides data by copying from a buffer. | ||
682 | * | ||
683 | * @param cls closure (points to the buffer) | ||
684 | * @param offset offset to read from; it is possible | ||
685 | * that the caller might need to go backwards | ||
686 | * a bit at times | ||
687 | * @param max maximum number of bytes that should be | ||
688 | * copied to @a buf; readers are not allowed | ||
689 | * to provide less data unless there is an error; | ||
690 | * a value of "0" will be used at the end to allow | ||
691 | * the reader to clean up its internal state | ||
692 | * @param buf where the reader should write the data | ||
693 | * @param emsg location for the reader to store an error message | ||
694 | * @return number of bytes written, usually @a max, 0 on error | ||
695 | */ | ||
696 | size_t | ||
697 | GNUNET_FS_data_reader_copy_ (void *cls, | ||
698 | uint64_t offset, | ||
699 | size_t max, | ||
700 | void *buf, | ||
701 | char **emsg); | ||
702 | |||
703 | |||
704 | /** | ||
705 | * Notification of FS that a search probe has made progress. | ||
706 | * This function is used INSTEAD of the client's event handler | ||
707 | * for downloads where the #GNUNET_FS_DOWNLOAD_IS_PROBE flag is set. | ||
708 | * | ||
709 | * @param cls closure, always NULL (!), actual closure | ||
710 | * is in the client-context of the info struct | ||
711 | * @param info details about the event, specifying the event type | ||
712 | * and various bits about the event | ||
713 | * @return client-context (for the next progress call | ||
714 | * for this operation; should be set to NULL for | ||
715 | * SUSPEND and STOPPED events). The value returned | ||
716 | * will be passed to future callbacks in the respective | ||
717 | * field in the `struct GNUNET_FS_ProgressInfo`. | ||
718 | */ | ||
719 | void * | ||
720 | GNUNET_FS_search_probe_progress_ (void *cls, | ||
721 | const struct GNUNET_FS_ProgressInfo *info); | ||
722 | |||
723 | |||
724 | /** | ||
725 | * Main function that performs the upload. | ||
726 | * | ||
727 | * @param cls `struct GNUNET_FS_PublishContext` identifies the upload | ||
728 | */ | ||
729 | void | ||
730 | GNUNET_FS_publish_main_ (void *cls); | ||
731 | |||
732 | |||
733 | /** | ||
734 | * Function called once the hash of the file | ||
735 | * that is being unindexed has been computed. | ||
736 | * | ||
737 | * @param cls closure, unindex context | ||
738 | * @param file_id computed hash, NULL on error | ||
739 | */ | ||
740 | void | ||
741 | GNUNET_FS_unindex_process_hash_ (void *cls, | ||
742 | const struct GNUNET_HashCode *file_id); | ||
743 | |||
744 | |||
745 | /** | ||
746 | * Extract the keywords for KBlock removal | ||
747 | * | ||
748 | * @param uc context for the unindex operation. | ||
749 | */ | ||
750 | void | ||
751 | GNUNET_FS_unindex_do_extract_keywords_ (struct GNUNET_FS_UnindexContext *uc); | ||
752 | |||
753 | |||
754 | /** | ||
755 | * If necessary, connect to the datastore and remove the KBlocks. | ||
756 | * | ||
757 | * @param uc context for the unindex operation. | ||
758 | */ | ||
759 | void | ||
760 | GNUNET_FS_unindex_do_remove_kblocks_ (struct GNUNET_FS_UnindexContext *uc); | ||
761 | |||
762 | |||
763 | /** | ||
764 | * Fill in all of the generic fields for a publish event and call the | ||
765 | * callback. | ||
766 | * | ||
767 | * @param pi structure to fill in | ||
768 | * @param pc overall publishing context | ||
769 | * @param p file information for the file being published | ||
770 | * @param offset where in the file are we so far | ||
771 | * @return value returned from callback | ||
772 | */ | ||
773 | void * | ||
774 | GNUNET_FS_publish_make_status_ (struct GNUNET_FS_ProgressInfo *pi, | ||
775 | struct GNUNET_FS_PublishContext *pc, | ||
776 | const struct GNUNET_FS_FileInformation *p, | ||
777 | uint64_t offset); | ||
778 | |||
779 | |||
780 | /** | ||
781 | * Fill in all of the generic fields for a download event and call the | ||
782 | * callback. | ||
783 | * | ||
784 | * @param pi structure to fill in | ||
785 | * @param dc overall download context | ||
786 | */ | ||
787 | void | ||
788 | GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi, | ||
789 | struct GNUNET_FS_DownloadContext *dc); | ||
790 | |||
791 | |||
792 | /** | ||
793 | * Task that creates the initial (top-level) download | ||
794 | * request for the file. | ||
795 | * | ||
796 | * @param cls the 'struct GNUNET_FS_DownloadContext' | ||
797 | */ | ||
798 | void | ||
799 | GNUNET_FS_download_start_task_ (void *cls); | ||
800 | |||
801 | |||
802 | /** | ||
803 | * Fill in all of the generic fields for | ||
804 | * an unindex event and call the callback. | ||
805 | * | ||
806 | * @param pi structure to fill in | ||
807 | * @param uc overall unindex context | ||
808 | * @param offset where we are in the file (for progress) | ||
809 | */ | ||
810 | void | ||
811 | GNUNET_FS_unindex_make_status_ (struct GNUNET_FS_ProgressInfo *pi, | ||
812 | struct GNUNET_FS_UnindexContext *uc, | ||
813 | uint64_t offset); | ||
814 | |||
815 | /** | ||
816 | * Fill in all of the generic fields for a search event and | ||
817 | * call the callback. | ||
818 | * | ||
819 | * @param pi structure to fill in | ||
820 | * @param h file-sharing handle | ||
821 | * @param sc overall search context | ||
822 | * @return value returned by the callback | ||
823 | */ | ||
824 | void * | ||
825 | GNUNET_FS_search_make_status_ (struct GNUNET_FS_ProgressInfo *pi, | ||
826 | struct GNUNET_FS_Handle *h, | ||
827 | struct GNUNET_FS_SearchContext *sc); | ||
828 | |||
829 | |||
830 | /** | ||
831 | * Connect to the datastore and remove the blocks. | ||
832 | * | ||
833 | * @param uc context for the unindex operation. | ||
834 | */ | ||
835 | void | ||
836 | GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc); | ||
837 | |||
838 | /** | ||
839 | * Build the request and actually initiate the search using the | ||
840 | * GNUnet FS service. | ||
841 | * | ||
842 | * @param sc search context | ||
843 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
844 | */ | ||
845 | int | ||
846 | GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc); | ||
847 | |||
848 | /** | ||
849 | * Start the downloading process (by entering the queue). | ||
850 | * | ||
851 | * @param dc our download context | ||
852 | */ | ||
853 | void | ||
854 | GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc); | ||
855 | |||
856 | |||
857 | /** | ||
858 | * Start download probes for the given search result. | ||
859 | * | ||
860 | * @param sr the search result | ||
861 | */ | ||
862 | void | ||
863 | GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr); | ||
864 | |||
865 | |||
866 | /** | ||
867 | * Remove serialization/deserialization file from disk. | ||
868 | * | ||
869 | * @param h master context | ||
870 | * @param ext component of the path | ||
871 | * @param ent entity identifier | ||
872 | */ | ||
873 | void | ||
874 | GNUNET_FS_remove_sync_file_ (struct GNUNET_FS_Handle *h, | ||
875 | const char *ext, | ||
876 | const char *ent); | ||
877 | |||
878 | |||
879 | /** | ||
880 | * Remove serialization/deserialization directory from disk. | ||
881 | * | ||
882 | * @param h master context | ||
883 | * @param ext component of the path | ||
884 | * @param uni unique name of parent | ||
885 | */ | ||
886 | void | ||
887 | GNUNET_FS_remove_sync_dir_ (struct GNUNET_FS_Handle *h, | ||
888 | const char *ext, | ||
889 | const char *uni); | ||
890 | |||
891 | |||
892 | /** | ||
893 | * Synchronize this file-information struct with its mirror | ||
894 | * on disk. Note that all internal FS-operations that change | ||
895 | * file information data should already call "sync" internally, | ||
896 | * so this function is likely not useful for clients. | ||
897 | * | ||
898 | * @param fi the struct to sync | ||
899 | */ | ||
900 | void | ||
901 | GNUNET_FS_file_information_sync_ (struct GNUNET_FS_FileInformation *f); | ||
902 | |||
903 | |||
904 | /** | ||
905 | * Synchronize this publishing struct with its mirror | ||
906 | * on disk. Note that all internal FS-operations that change | ||
907 | * publishing structs should already call "sync" internally, | ||
908 | * so this function is likely not useful for clients. | ||
909 | * | ||
910 | * @param pc the struct to sync | ||
911 | */ | ||
912 | void | ||
913 | GNUNET_FS_publish_sync_ (struct GNUNET_FS_PublishContext *pc); | ||
914 | |||
915 | |||
916 | /** | ||
917 | * Synchronize this unindex struct with its mirror | ||
918 | * on disk. Note that all internal FS-operations that change | ||
919 | * publishing structs should already call "sync" internally, | ||
920 | * so this function is likely not useful for clients. | ||
921 | * | ||
922 | * @param uc the struct to sync | ||
923 | */ | ||
924 | void | ||
925 | GNUNET_FS_unindex_sync_ (struct GNUNET_FS_UnindexContext *uc); | ||
926 | |||
927 | |||
928 | /** | ||
929 | * Synchronize this search struct with its mirror | ||
930 | * on disk. Note that all internal FS-operations that change | ||
931 | * publishing structs should already call "sync" internally, | ||
932 | * so this function is likely not useful for clients. | ||
933 | * | ||
934 | * @param sc the struct to sync | ||
935 | */ | ||
936 | void | ||
937 | GNUNET_FS_search_sync_ (struct GNUNET_FS_SearchContext *sc); | ||
938 | |||
939 | |||
940 | /** | ||
941 | * Synchronize this search result with its mirror | ||
942 | * on disk. Note that all internal FS-operations that change | ||
943 | * publishing structs should already call "sync" internally, | ||
944 | * so this function is likely not useful for clients. | ||
945 | * | ||
946 | * @param sr the struct to sync | ||
947 | */ | ||
948 | void | ||
949 | GNUNET_FS_search_result_sync_ (struct GNUNET_FS_SearchResult *sr); | ||
950 | |||
951 | |||
952 | /** | ||
953 | * Synchronize this download struct with its mirror | ||
954 | * on disk. Note that all internal FS-operations that change | ||
955 | * publishing structs should already call "sync" internally, | ||
956 | * so this function is likely not useful for clients. | ||
957 | * | ||
958 | * @param dc the struct to sync | ||
959 | */ | ||
960 | void | ||
961 | GNUNET_FS_download_sync_ (struct GNUNET_FS_DownloadContext *dc); | ||
962 | |||
963 | |||
964 | /** | ||
965 | * Create SUSPEND event for the given publish operation | ||
966 | * and then clean up our state (without stop signal). | ||
967 | * | ||
968 | * @param cls the `struct GNUNET_FS_PublishContext` to signal for | ||
969 | */ | ||
970 | void | ||
971 | GNUNET_FS_publish_signal_suspend_ (void *cls); | ||
972 | |||
973 | |||
974 | /** | ||
975 | * Create SUSPEND event for the given search operation | ||
976 | * and then clean up our state (without stop signal). | ||
977 | * | ||
978 | * @param cls the 'struct GNUNET_FS_SearchContext' to signal for | ||
979 | */ | ||
980 | void | ||
981 | GNUNET_FS_search_signal_suspend_ (void *cls); | ||
982 | |||
983 | |||
984 | /** | ||
985 | * Create SUSPEND event for the given download operation | ||
986 | * and then clean up our state (without stop signal). | ||
987 | * | ||
988 | * @param cls the `struct GNUNET_FS_DownloadContext` to signal for | ||
989 | */ | ||
990 | void | ||
991 | GNUNET_FS_download_signal_suspend_ (void *cls); | ||
992 | |||
993 | |||
994 | /** | ||
995 | * Create SUSPEND event for the given unindex operation | ||
996 | * and then clean up our state (without stop signal). | ||
997 | * | ||
998 | * @param cls the `struct GNUNET_FS_UnindexContext` to signal for | ||
999 | */ | ||
1000 | void | ||
1001 | GNUNET_FS_unindex_signal_suspend_ (void *cls); | ||
1002 | |||
1003 | |||
1004 | /** | ||
1005 | * Function signature of the functions that can be called | ||
1006 | * to trigger suspend signals and clean-up for top-level | ||
1007 | * activities. | ||
1008 | * | ||
1009 | * @param cls closure | ||
1010 | */ | ||
1011 | typedef void (*SuspendSignalFunction) (void *cls); | ||
1012 | |||
1013 | /** | ||
1014 | * We track all of the top-level activities of FS | ||
1015 | * so that we can signal 'suspend' on shutdown. | ||
1016 | */ | ||
1017 | struct TopLevelActivity | ||
1018 | { | ||
1019 | /** | ||
1020 | * This is a doubly-linked list. | ||
1021 | */ | ||
1022 | struct TopLevelActivity *next; | ||
1023 | |||
1024 | /** | ||
1025 | * This is a doubly-linked list. | ||
1026 | */ | ||
1027 | struct TopLevelActivity *prev; | ||
1028 | |||
1029 | /** | ||
1030 | * Function to call for suspend-signalling and clean up. | ||
1031 | */ | ||
1032 | SuspendSignalFunction ssf; | ||
1033 | |||
1034 | /** | ||
1035 | * Closure for 'ssf' (some struct GNUNET_FS_XXXHandle*) | ||
1036 | */ | ||
1037 | void *ssf_cls; | ||
1038 | }; | ||
1039 | |||
1040 | |||
1041 | /** | ||
1042 | * Create a top-level activity entry. | ||
1043 | * | ||
1044 | * @param h global fs handle | ||
1045 | * @param ssf suspend signal function to use | ||
1046 | * @param ssf_cls closure for @a ssf | ||
1047 | * @return fresh top-level activity handle | ||
1048 | */ | ||
1049 | struct TopLevelActivity * | ||
1050 | GNUNET_FS_make_top (struct GNUNET_FS_Handle *h, | ||
1051 | SuspendSignalFunction ssf, | ||
1052 | void *ssf_cls); | ||
1053 | |||
1054 | |||
1055 | /** | ||
1056 | * Destroy a top-level activity entry. | ||
1057 | * | ||
1058 | * @param h global fs handle | ||
1059 | * @param top top level activity entry | ||
1060 | */ | ||
1061 | void | ||
1062 | GNUNET_FS_end_top (struct GNUNET_FS_Handle *h, | ||
1063 | struct TopLevelActivity *top); | ||
1064 | |||
1065 | |||
1066 | /** | ||
1067 | * Master context for most FS operations. | ||
1068 | */ | ||
1069 | struct GNUNET_FS_Handle | ||
1070 | { | ||
1071 | /** | ||
1072 | * Configuration to use. | ||
1073 | */ | ||
1074 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
1075 | |||
1076 | /** | ||
1077 | * Name of our client. | ||
1078 | */ | ||
1079 | char *client_name; | ||
1080 | |||
1081 | /** | ||
1082 | * Function to call with updates on our progress. | ||
1083 | */ | ||
1084 | GNUNET_FS_ProgressCallback upcb; | ||
1085 | |||
1086 | /** | ||
1087 | * Closure for upcb. | ||
1088 | */ | ||
1089 | void *upcb_cls; | ||
1090 | |||
1091 | /** | ||
1092 | * Head of DLL of top-level activities. | ||
1093 | */ | ||
1094 | struct TopLevelActivity *top_head; | ||
1095 | |||
1096 | /** | ||
1097 | * Tail of DLL of top-level activities. | ||
1098 | */ | ||
1099 | struct TopLevelActivity *top_tail; | ||
1100 | |||
1101 | /** | ||
1102 | * Head of DLL of running jobs. | ||
1103 | */ | ||
1104 | struct GNUNET_FS_QueueEntry *running_head; | ||
1105 | |||
1106 | /** | ||
1107 | * Tail of DLL of running jobs. | ||
1108 | */ | ||
1109 | struct GNUNET_FS_QueueEntry *running_tail; | ||
1110 | |||
1111 | /** | ||
1112 | * Head of DLL of pending jobs. | ||
1113 | */ | ||
1114 | struct GNUNET_FS_QueueEntry *pending_head; | ||
1115 | |||
1116 | /** | ||
1117 | * Tail of DLL of pending jobs. | ||
1118 | */ | ||
1119 | struct GNUNET_FS_QueueEntry *pending_tail; | ||
1120 | |||
1121 | /** | ||
1122 | * Head of active probes. | ||
1123 | */ | ||
1124 | struct GNUNET_FS_SearchResult *probes_head; | ||
1125 | |||
1126 | /** | ||
1127 | * Tail of active probes. | ||
1128 | */ | ||
1129 | struct GNUNET_FS_SearchResult *probes_tail; | ||
1130 | |||
1131 | /** | ||
1132 | * Task that processes the jobs in the running and pending queues | ||
1133 | * (and moves jobs around as needed). | ||
1134 | */ | ||
1135 | struct GNUNET_SCHEDULER_Task *queue_job; | ||
1136 | |||
1137 | /** | ||
1138 | * Task we use to report periodically to the application that | ||
1139 | * certain search probes (from @e probes_head) are still running. | ||
1140 | */ | ||
1141 | struct GNUNET_SCHEDULER_Task *probe_ping_task; | ||
1142 | |||
1143 | /** | ||
1144 | * Average time we take for a single request to be satisfied. | ||
1145 | * FIXME: not yet calculated properly... | ||
1146 | */ | ||
1147 | struct GNUNET_TIME_Relative avg_block_latency; | ||
1148 | |||
1149 | /** | ||
1150 | * How many actual downloads do we have running right now? | ||
1151 | */ | ||
1152 | unsigned int active_downloads; | ||
1153 | |||
1154 | /** | ||
1155 | * How many blocks do the active downloads have? | ||
1156 | */ | ||
1157 | unsigned int active_blocks; | ||
1158 | |||
1159 | /** | ||
1160 | * General flags. | ||
1161 | */ | ||
1162 | enum GNUNET_FS_Flags flags; | ||
1163 | |||
1164 | /** | ||
1165 | * Maximum number of parallel downloads. | ||
1166 | */ | ||
1167 | unsigned int max_parallel_downloads; | ||
1168 | |||
1169 | /** | ||
1170 | * Maximum number of parallel requests. | ||
1171 | */ | ||
1172 | unsigned int max_parallel_requests; | ||
1173 | }; | ||
1174 | |||
1175 | |||
1176 | /** | ||
1177 | * Handle for controlling a publication process. | ||
1178 | */ | ||
1179 | struct GNUNET_FS_PublishContext | ||
1180 | { | ||
1181 | /** | ||
1182 | * Handle to the global fs context. | ||
1183 | */ | ||
1184 | struct GNUNET_FS_Handle *h; | ||
1185 | |||
1186 | /** | ||
1187 | * Our top-level activity entry (if we are top-level, otherwise NULL). | ||
1188 | */ | ||
1189 | struct TopLevelActivity *top; | ||
1190 | |||
1191 | /** | ||
1192 | * File-structure that is being shared. | ||
1193 | */ | ||
1194 | struct GNUNET_FS_FileInformation *fi; | ||
1195 | |||
1196 | /** | ||
1197 | * Namespace that we are publishing in, NULL if we have no namespace. | ||
1198 | */ | ||
1199 | struct GNUNET_CRYPTO_EcdsaPrivateKey *ns; | ||
1200 | |||
1201 | /** | ||
1202 | * ID of the content in the namespace, NULL if we have no namespace. | ||
1203 | */ | ||
1204 | char *nid; | ||
1205 | |||
1206 | /** | ||
1207 | * ID for future updates, NULL if we have no namespace or no updates. | ||
1208 | */ | ||
1209 | char *nuid; | ||
1210 | |||
1211 | /** | ||
1212 | * Filename used for serializing information about this operation | ||
1213 | * (should be determined using 'mktemp'). | ||
1214 | */ | ||
1215 | char *serialization; | ||
1216 | |||
1217 | /** | ||
1218 | * Our own message queue for the FS service; only briefly used when | ||
1219 | * we start to index a file, otherwise NULL. | ||
1220 | */ | ||
1221 | struct GNUNET_MQ_Handle *mq; | ||
1222 | |||
1223 | /** | ||
1224 | * Current position in the file-tree for the upload. | ||
1225 | */ | ||
1226 | struct GNUNET_FS_FileInformation *fi_pos; | ||
1227 | |||
1228 | /** | ||
1229 | * Non-null if we are currently hashing a file. | ||
1230 | */ | ||
1231 | struct GNUNET_CRYPTO_FileHashContext *fhc; | ||
1232 | |||
1233 | /** | ||
1234 | * Connection to the datastore service. | ||
1235 | */ | ||
1236 | struct GNUNET_DATASTORE_Handle *dsh; | ||
1237 | |||
1238 | /** | ||
1239 | * Queue entry for reservation/unreservation. | ||
1240 | */ | ||
1241 | struct GNUNET_DATASTORE_QueueEntry *qre; | ||
1242 | |||
1243 | /** | ||
1244 | * Context for SKS publishing operation that is part of this publishing operation | ||
1245 | * (NULL if not active). | ||
1246 | */ | ||
1247 | struct GNUNET_FS_PublishSksContext *sks_pc; | ||
1248 | |||
1249 | /** | ||
1250 | * Context for KSK publishing operation that is part of this publishing operation | ||
1251 | * (NULL if not active). | ||
1252 | */ | ||
1253 | struct GNUNET_FS_PublishKskContext *ksk_pc; | ||
1254 | |||
1255 | /** | ||
1256 | * ID of the task performing the upload. NO_TASK if the upload has | ||
1257 | * completed. | ||
1258 | */ | ||
1259 | struct GNUNET_SCHEDULER_Task *upload_task; | ||
1260 | |||
1261 | /** | ||
1262 | * Storage space to reserve for the operation. | ||
1263 | */ | ||
1264 | uint64_t reserve_space; | ||
1265 | |||
1266 | /** | ||
1267 | * Overall number of entries to reserve for the | ||
1268 | * publish operation. | ||
1269 | */ | ||
1270 | uint32_t reserve_entries; | ||
1271 | |||
1272 | /** | ||
1273 | * Options for publishing. | ||
1274 | */ | ||
1275 | enum GNUNET_FS_PublishOptions options; | ||
1276 | |||
1277 | /** | ||
1278 | * Space reservation ID with datastore service | ||
1279 | * for this upload. | ||
1280 | */ | ||
1281 | int rid; | ||
1282 | |||
1283 | /** | ||
1284 | * Set to #GNUNET_YES if we were able to publish any block. | ||
1285 | * (and thus unindexing on error might make sense). | ||
1286 | */ | ||
1287 | int any_done; | ||
1288 | |||
1289 | /** | ||
1290 | * Set to #GNUNET_YES if all processing has completed. | ||
1291 | */ | ||
1292 | int all_done; | ||
1293 | |||
1294 | /** | ||
1295 | * Flag set to #GNUNET_YES if the next callback from | ||
1296 | * #GNUNET_FS_file_information_inspect should be skipped because it | ||
1297 | * is for the directory which was already processed with the parent. | ||
1298 | */ | ||
1299 | int skip_next_fi_callback; | ||
1300 | }; | ||
1301 | |||
1302 | |||
1303 | /** | ||
1304 | * Phases of unindex processing (state machine). | ||
1305 | */ | ||
1306 | enum UnindexState | ||
1307 | { | ||
1308 | /** | ||
1309 | * We're currently hashing the file. | ||
1310 | */ | ||
1311 | UNINDEX_STATE_HASHING = 0, | ||
1312 | |||
1313 | /** | ||
1314 | * We're telling the datastore to delete | ||
1315 | * the respective DBlocks and IBlocks. | ||
1316 | */ | ||
1317 | UNINDEX_STATE_DS_REMOVE = 1, | ||
1318 | |||
1319 | /** | ||
1320 | * Find out which keywords apply. | ||
1321 | */ | ||
1322 | UNINDEX_STATE_EXTRACT_KEYWORDS = 2, | ||
1323 | |||
1324 | /** | ||
1325 | * We're telling the datastore to remove KBlocks. | ||
1326 | */ | ||
1327 | UNINDEX_STATE_DS_REMOVE_KBLOCKS = 3, | ||
1328 | |||
1329 | /** | ||
1330 | * We're notifying the FS service about | ||
1331 | * the unindexing. | ||
1332 | */ | ||
1333 | UNINDEX_STATE_FS_NOTIFY = 4, | ||
1334 | |||
1335 | /** | ||
1336 | * We're done. | ||
1337 | */ | ||
1338 | UNINDEX_STATE_COMPLETE = 5, | ||
1339 | |||
1340 | /** | ||
1341 | * We've encountered a fatal error. | ||
1342 | */ | ||
1343 | UNINDEX_STATE_ERROR = 6 | ||
1344 | }; | ||
1345 | |||
1346 | |||
1347 | /** | ||
1348 | * Handle for controlling an unindexing operation. | ||
1349 | */ | ||
1350 | struct GNUNET_FS_UnindexContext | ||
1351 | { | ||
1352 | /** | ||
1353 | * The content hash key of the last block we processed, will in the | ||
1354 | * end be set to the CHK from the URI. Used to remove the KBlocks. | ||
1355 | */ | ||
1356 | struct ContentHashKey chk; | ||
1357 | |||
1358 | /** | ||
1359 | * Global FS context. | ||
1360 | */ | ||
1361 | struct GNUNET_FS_Handle *h; | ||
1362 | |||
1363 | /** | ||
1364 | * Our top-level activity entry. | ||
1365 | */ | ||
1366 | struct TopLevelActivity *top; | ||
1367 | |||
1368 | /** | ||
1369 | * Directory scanner to find keywords (KBlock removal). | ||
1370 | */ | ||
1371 | struct GNUNET_FS_DirScanner *dscan; | ||
1372 | |||
1373 | /** | ||
1374 | * Keywords found (telling us which KBlocks to remove). | ||
1375 | */ | ||
1376 | struct GNUNET_FS_Uri *ksk_uri; | ||
1377 | |||
1378 | /** | ||
1379 | * Current offset in KSK removal. | ||
1380 | */ | ||
1381 | uint32_t ksk_offset; | ||
1382 | |||
1383 | /** | ||
1384 | * Name of the file that we are unindexing. | ||
1385 | */ | ||
1386 | char *filename; | ||
1387 | |||
1388 | /** | ||
1389 | * Short name under which we are serializing the state of this operation. | ||
1390 | */ | ||
1391 | char *serialization; | ||
1392 | |||
1393 | /** | ||
1394 | * Connection to the FS service, only valid during the | ||
1395 | * #UNINDEX_STATE_FS_NOTIFY phase. | ||
1396 | */ | ||
1397 | struct GNUNET_MQ_Handle *mq; | ||
1398 | |||
1399 | /** | ||
1400 | * Connection to the datastore service, only valid during the | ||
1401 | * UNINDEX_STATE_DS_NOTIFY phase. | ||
1402 | */ | ||
1403 | struct GNUNET_DATASTORE_Handle *dsh; | ||
1404 | |||
1405 | /** | ||
1406 | * Pointer kept for the client. | ||
1407 | */ | ||
1408 | void *client_info; | ||
1409 | |||
1410 | /** | ||
1411 | * Merkle-ish tree encoder context. | ||
1412 | */ | ||
1413 | struct GNUNET_FS_TreeEncoder *tc; | ||
1414 | |||
1415 | /** | ||
1416 | * Handle used to read the file. | ||
1417 | */ | ||
1418 | struct GNUNET_DISK_FileHandle *fh; | ||
1419 | |||
1420 | /** | ||
1421 | * Handle to datastore 'get_key' operation issued for | ||
1422 | * obtaining KBlocks. | ||
1423 | */ | ||
1424 | struct GNUNET_DATASTORE_QueueEntry *dqe; | ||
1425 | |||
1426 | /** | ||
1427 | * Current key for decrypting UBLocks from 'get_key' operation. | ||
1428 | */ | ||
1429 | struct GNUNET_HashCode ukey; | ||
1430 | |||
1431 | /** | ||
1432 | * Current query of 'get_key' operation. | ||
1433 | */ | ||
1434 | struct GNUNET_HashCode uquery; | ||
1435 | |||
1436 | /** | ||
1437 | * Error message, NULL on success. | ||
1438 | */ | ||
1439 | char *emsg; | ||
1440 | |||
1441 | /** | ||
1442 | * Context for hashing of the file. | ||
1443 | */ | ||
1444 | struct GNUNET_CRYPTO_FileHashContext *fhc; | ||
1445 | |||
1446 | /** | ||
1447 | * Overall size of the file. | ||
1448 | */ | ||
1449 | uint64_t file_size; | ||
1450 | |||
1451 | /** | ||
1452 | * When did we start? | ||
1453 | */ | ||
1454 | struct GNUNET_TIME_Absolute start_time; | ||
1455 | |||
1456 | /** | ||
1457 | * Hash of the file's contents (once computed). | ||
1458 | */ | ||
1459 | struct GNUNET_HashCode file_id; | ||
1460 | |||
1461 | /** | ||
1462 | * Current operatinonal phase. | ||
1463 | */ | ||
1464 | enum UnindexState state; | ||
1465 | }; | ||
1466 | |||
1467 | |||
1468 | /** | ||
1469 | * Information we keep for each keyword in a keyword search. | ||
1470 | */ | ||
1471 | struct SearchRequestEntry | ||
1472 | { | ||
1473 | /** | ||
1474 | * Hash of the public key, also known as the query. | ||
1475 | */ | ||
1476 | struct GNUNET_HashCode uquery; | ||
1477 | |||
1478 | /** | ||
1479 | * Derived public key, hashes to 'uquery'. | ||
1480 | */ | ||
1481 | struct GNUNET_CRYPTO_EcdsaPublicKey dpub; | ||
1482 | |||
1483 | /** | ||
1484 | * The original keyword, used to derive the | ||
1485 | * key (for decrypting the UBlock). | ||
1486 | */ | ||
1487 | char *keyword; | ||
1488 | |||
1489 | /** | ||
1490 | * Map that contains a "struct GNUNET_FS_SearchResult" for each result that | ||
1491 | * was found under this keyword. Note that the entries will point | ||
1492 | * to the same locations as those in the master result map (in | ||
1493 | * "struct GNUNET_FS_SearchContext"), so they should not be freed. | ||
1494 | * The key for each entry is the XOR of the key and query in the CHK | ||
1495 | * URI (as a unique identifier for the search result). | ||
1496 | */ | ||
1497 | struct GNUNET_CONTAINER_MultiHashMap *results; | ||
1498 | |||
1499 | /** | ||
1500 | * Is this keyword a mandatory keyword | ||
1501 | * (started with '+')? | ||
1502 | */ | ||
1503 | int mandatory; | ||
1504 | }; | ||
1505 | |||
1506 | |||
1507 | /** | ||
1508 | * Handle for controlling a search. | ||
1509 | */ | ||
1510 | struct GNUNET_FS_SearchContext | ||
1511 | { | ||
1512 | /** | ||
1513 | * Handle to the global FS context. | ||
1514 | */ | ||
1515 | struct GNUNET_FS_Handle *h; | ||
1516 | |||
1517 | /** | ||
1518 | * Our top-level activity entry (if we are top-level, otherwise NULL). | ||
1519 | */ | ||
1520 | struct TopLevelActivity *top; | ||
1521 | |||
1522 | /** | ||
1523 | * List of keywords that we're looking for. | ||
1524 | */ | ||
1525 | struct GNUNET_FS_Uri *uri; | ||
1526 | |||
1527 | /** | ||
1528 | * For update-searches, link to the search result that triggered | ||
1529 | * the update search; otherwise NULL. | ||
1530 | */ | ||
1531 | struct GNUNET_FS_SearchResult *psearch_result; | ||
1532 | |||
1533 | /** | ||
1534 | * Connection to the FS service. | ||
1535 | */ | ||
1536 | struct GNUNET_MQ_Handle *mq; | ||
1537 | |||
1538 | /** | ||
1539 | * Pointer we keep for the client. | ||
1540 | */ | ||
1541 | void *client_info; | ||
1542 | |||
1543 | /** | ||
1544 | * Name of the file on disk we use for persistence. | ||
1545 | */ | ||
1546 | char *serialization; | ||
1547 | |||
1548 | /** | ||
1549 | * Error message (non-NULL if this operation failed). | ||
1550 | */ | ||
1551 | char *emsg; | ||
1552 | |||
1553 | /** | ||
1554 | * Map that contains a `struct GNUNET_FS_SearchResult` for each result that | ||
1555 | * was found in the search. The key for each entry is the XOR of | ||
1556 | * the key and query in the CHK URI (as a unique identifier for the | ||
1557 | * search result). | ||
1558 | */ | ||
1559 | struct GNUNET_CONTAINER_MultiHashMap *master_result_map; | ||
1560 | |||
1561 | /** | ||
1562 | * Per-keyword information for a keyword search. This array will | ||
1563 | * have exactly as many entries as there were keywords. | ||
1564 | */ | ||
1565 | struct SearchRequestEntry *requests; | ||
1566 | |||
1567 | /** | ||
1568 | * When did we start? | ||
1569 | */ | ||
1570 | struct GNUNET_TIME_Absolute start_time; | ||
1571 | |||
1572 | /** | ||
1573 | * How long to wait before we try to reconnect to FS service? | ||
1574 | */ | ||
1575 | struct GNUNET_TIME_Relative reconnect_backoff; | ||
1576 | |||
1577 | /** | ||
1578 | * ID of a task that is using this struct and that must be cancelled | ||
1579 | * when the search is being stopped (if not | ||
1580 | * NULL). Used for the task that adds some | ||
1581 | * artificial delay when trying to reconnect to the FS service. | ||
1582 | */ | ||
1583 | struct GNUNET_SCHEDULER_Task *task; | ||
1584 | |||
1585 | /** | ||
1586 | * Anonymity level for the search. | ||
1587 | */ | ||
1588 | uint32_t anonymity; | ||
1589 | |||
1590 | /** | ||
1591 | * Number of mandatory keywords in this query. | ||
1592 | */ | ||
1593 | uint32_t mandatory_count; | ||
1594 | |||
1595 | /** | ||
1596 | * Options for the search. | ||
1597 | */ | ||
1598 | enum GNUNET_FS_SearchOptions options; | ||
1599 | }; | ||
1600 | |||
1601 | |||
1602 | /** | ||
1603 | * FSM for possible states a block can go through. The typical | ||
1604 | * order of progression is linear through the states, alternatives | ||
1605 | * are documented in the comments. | ||
1606 | */ | ||
1607 | enum BlockRequestState | ||
1608 | { | ||
1609 | /** | ||
1610 | * Initial state, block has only been allocated (since it is | ||
1611 | * relevant to the overall download request). | ||
1612 | */ | ||
1613 | BRS_INIT = 0, | ||
1614 | |||
1615 | /** | ||
1616 | * We've checked the block on the path down the tree, and the | ||
1617 | * content on disk did match the desired CHK, but not all | ||
1618 | * the way down, so at the bottom some blocks will still | ||
1619 | * need to be reconstructed). | ||
1620 | */ | ||
1621 | BRS_RECONSTRUCT_DOWN = 1, | ||
1622 | |||
1623 | /** | ||
1624 | * We've calculated the CHK bottom-up based on the meta data. | ||
1625 | * This may work, but if it did we have to write the meta data to | ||
1626 | * disk at the end (and we still need to check against the | ||
1627 | * CHK set on top). | ||
1628 | */ | ||
1629 | BRS_RECONSTRUCT_META_UP = 2, | ||
1630 | |||
1631 | /** | ||
1632 | * We've calculated the CHK bottom-up based on what we have on | ||
1633 | * disk, which may not be what the desired CHK is. If the | ||
1634 | * reconstructed CHKs match whatever comes from above, we're | ||
1635 | * done with the respective subtree. | ||
1636 | */ | ||
1637 | BRS_RECONSTRUCT_UP = 3, | ||
1638 | |||
1639 | /** | ||
1640 | * We've determined the real, desired CHK for this block | ||
1641 | * (full tree reconstruction failed), request is now pending. | ||
1642 | * If the CHK that bubbled up through reconstruction did match | ||
1643 | * the top-level request, the state machine for the subtree | ||
1644 | * would have moved to BRS_DOWNLOAD_UP. | ||
1645 | */ | ||
1646 | BRS_CHK_SET = 4, | ||
1647 | |||
1648 | /** | ||
1649 | * We've successfully downloaded this block, but the children | ||
1650 | * still need to be either downloaded or verified (download | ||
1651 | * request propagates down). If the download fails, the | ||
1652 | * state machine for this block may move to | ||
1653 | * BRS_DOWNLOAD_ERROR instead. | ||
1654 | */ | ||
1655 | BRS_DOWNLOAD_DOWN = 5, | ||
1656 | |||
1657 | /** | ||
1658 | * This block and all of its children have been downloaded | ||
1659 | * successfully (full completion propagates up). | ||
1660 | */ | ||
1661 | BRS_DOWNLOAD_UP = 6, | ||
1662 | |||
1663 | /** | ||
1664 | * We got a block back that matched the query but did not hash to | ||
1665 | * the key (malicious publisher or hash collision); this block | ||
1666 | * can never be downloaded (error propagates up). | ||
1667 | */ | ||
1668 | BRS_ERROR = 7 | ||
1669 | }; | ||
1670 | |||
1671 | |||
1672 | /** | ||
1673 | * Information about an active download request. | ||
1674 | */ | ||
1675 | struct DownloadRequest | ||
1676 | { | ||
1677 | /** | ||
1678 | * Parent in the CHK-tree. | ||
1679 | */ | ||
1680 | struct DownloadRequest *parent; | ||
1681 | |||
1682 | /** | ||
1683 | * Array (!) of child-requests, or NULL for the bottom of the tree. | ||
1684 | */ | ||
1685 | struct DownloadRequest **children; | ||
1686 | |||
1687 | /** | ||
1688 | * CHK for the request for this block (set during reconstruction | ||
1689 | * to what we have on disk, later to what we want to have). | ||
1690 | */ | ||
1691 | struct ContentHashKey chk; | ||
1692 | |||
1693 | /** | ||
1694 | * Offset of the corresponding block. Specifically, first (!) byte of | ||
1695 | * the first DBLOCK in the subtree induced by block represented by | ||
1696 | * this request. | ||
1697 | */ | ||
1698 | uint64_t offset; | ||
1699 | |||
1700 | /** | ||
1701 | * Number of entries in @e children array. | ||
1702 | */ | ||
1703 | unsigned int num_children; | ||
1704 | |||
1705 | /** | ||
1706 | * Depth of the corresponding block in the tree. 0==DBLOCKs. | ||
1707 | */ | ||
1708 | unsigned int depth; | ||
1709 | |||
1710 | /** | ||
1711 | * Offset of the CHK for this block in the parent block | ||
1712 | */ | ||
1713 | unsigned int chk_idx; | ||
1714 | |||
1715 | /** | ||
1716 | * State in the FSM. | ||
1717 | */ | ||
1718 | enum BlockRequestState state; | ||
1719 | }; | ||
1720 | |||
1721 | |||
1722 | /** | ||
1723 | * (recursively) free download request structure | ||
1724 | * | ||
1725 | * @param dr request to free | ||
1726 | */ | ||
1727 | void | ||
1728 | GNUNET_FS_free_download_request_ (struct DownloadRequest *dr); | ||
1729 | |||
1730 | |||
1731 | /** | ||
1732 | * Stop the ping task for this search result. | ||
1733 | * | ||
1734 | * @param sr result to start pinging for. | ||
1735 | */ | ||
1736 | void | ||
1737 | GNUNET_FS_stop_probe_ping_task_ (struct GNUNET_FS_SearchResult *sr); | ||
1738 | |||
1739 | |||
1740 | /** | ||
1741 | * Context for controlling a download. | ||
1742 | */ | ||
1743 | struct GNUNET_FS_DownloadContext | ||
1744 | { | ||
1745 | /** | ||
1746 | * Global FS context. | ||
1747 | */ | ||
1748 | struct GNUNET_FS_Handle *h; | ||
1749 | |||
1750 | /** | ||
1751 | * Our top-level activity entry (if we are top-level, otherwise NULL). | ||
1752 | */ | ||
1753 | struct TopLevelActivity *top; | ||
1754 | |||
1755 | /** | ||
1756 | * Connection to the FS service. | ||
1757 | */ | ||
1758 | struct GNUNET_MQ_Handle *mq; | ||
1759 | |||
1760 | /** | ||
1761 | * Parent download (used when downloading files | ||
1762 | * in directories). | ||
1763 | */ | ||
1764 | struct GNUNET_FS_DownloadContext *parent; | ||
1765 | |||
1766 | /** | ||
1767 | * Associated search (used when downloading files | ||
1768 | * based on search results), or NULL for none. | ||
1769 | */ | ||
1770 | struct GNUNET_FS_SearchResult *search; | ||
1771 | |||
1772 | /** | ||
1773 | * Head of list of child downloads. | ||
1774 | */ | ||
1775 | struct GNUNET_FS_DownloadContext *child_head; | ||
1776 | |||
1777 | /** | ||
1778 | * Tail of list of child downloads. | ||
1779 | */ | ||
1780 | struct GNUNET_FS_DownloadContext *child_tail; | ||
1781 | |||
1782 | /** | ||
1783 | * Previous download belonging to the same parent. | ||
1784 | */ | ||
1785 | struct GNUNET_FS_DownloadContext *prev; | ||
1786 | |||
1787 | /** | ||
1788 | * Next download belonging to the same parent. | ||
1789 | */ | ||
1790 | struct GNUNET_FS_DownloadContext *next; | ||
1791 | |||
1792 | /** | ||
1793 | * Context kept for the client. | ||
1794 | */ | ||
1795 | void *client_info; | ||
1796 | |||
1797 | /** | ||
1798 | * URI that identifies the file that we are downloading. | ||
1799 | */ | ||
1800 | struct GNUNET_FS_Uri *uri; | ||
1801 | |||
1802 | /** | ||
1803 | * Known meta-data for the file (can be NULL). | ||
1804 | */ | ||
1805 | struct GNUNET_FS_MetaData *meta; | ||
1806 | |||
1807 | /** | ||
1808 | * Error message, NULL if we're doing OK. | ||
1809 | */ | ||
1810 | char *emsg; | ||
1811 | |||
1812 | /** | ||
1813 | * Random portion of filename we use for syncing state of this | ||
1814 | * download. | ||
1815 | */ | ||
1816 | char *serialization; | ||
1817 | |||
1818 | /** | ||
1819 | * Where are we writing the data (name of the | ||
1820 | * file, can be NULL!). | ||
1821 | */ | ||
1822 | char *filename; | ||
1823 | |||
1824 | /** | ||
1825 | * Where are we writing the data temporarily (name of the | ||
1826 | * file, can be NULL!); used if we do not have a permanent | ||
1827 | * name and we are a directory and we do a recursive download. | ||
1828 | */ | ||
1829 | char *temp_filename; | ||
1830 | |||
1831 | /** | ||
1832 | * Our entry in the job queue. | ||
1833 | */ | ||
1834 | struct GNUNET_FS_QueueEntry *job_queue; | ||
1835 | |||
1836 | /** | ||
1837 | * Tree encoder used for the reconstruction. | ||
1838 | */ | ||
1839 | struct GNUNET_FS_TreeEncoder *te; | ||
1840 | |||
1841 | /** | ||
1842 | * File handle for reading data from an existing file | ||
1843 | * (to pass to tree encoder). | ||
1844 | */ | ||
1845 | struct GNUNET_DISK_FileHandle *rfh; | ||
1846 | |||
1847 | /** | ||
1848 | * Map of active requests (those waiting for a response). The key | ||
1849 | * is the hash of the encryped block (aka query). | ||
1850 | */ | ||
1851 | struct GNUNET_CONTAINER_MultiHashMap *active; | ||
1852 | |||
1853 | /** | ||
1854 | * Top-level download request. | ||
1855 | */ | ||
1856 | struct DownloadRequest *top_request; | ||
1857 | |||
1858 | /** | ||
1859 | * Identity of the peer having the content, or all-zeros | ||
1860 | * if we don't know of such a peer. | ||
1861 | */ | ||
1862 | struct GNUNET_PeerIdentity target; | ||
1863 | |||
1864 | /** | ||
1865 | * ID of a task that is using this struct and that must be cancelled | ||
1866 | * when the download is being stopped (if not | ||
1867 | * NULL). Used for the task that adds some | ||
1868 | * artificial delay when trying to reconnect to the FS service or | ||
1869 | * the task processing incrementally the data on disk, or the | ||
1870 | * task requesting blocks, etc. | ||
1871 | */ | ||
1872 | struct GNUNET_SCHEDULER_Task *task; | ||
1873 | |||
1874 | /** | ||
1875 | * What is the first offset that we're interested | ||
1876 | * in? | ||
1877 | */ | ||
1878 | uint64_t offset; | ||
1879 | |||
1880 | /** | ||
1881 | * How many bytes starting from offset are desired? | ||
1882 | * This is NOT the overall length of the file! | ||
1883 | */ | ||
1884 | uint64_t length; | ||
1885 | |||
1886 | /** | ||
1887 | * How many bytes have we already received within | ||
1888 | * the specified range (DBlocks only). | ||
1889 | */ | ||
1890 | uint64_t completed; | ||
1891 | |||
1892 | /** | ||
1893 | * What was the size of the file on disk that we're downloading | ||
1894 | * before we started? Used to detect if there is a point in | ||
1895 | * checking an existing block on disk for matching the desired | ||
1896 | * content. 0 if the file did not exist already. | ||
1897 | */ | ||
1898 | uint64_t old_file_size; | ||
1899 | |||
1900 | /** | ||
1901 | * Time download was started. | ||
1902 | */ | ||
1903 | struct GNUNET_TIME_Absolute start_time; | ||
1904 | |||
1905 | /** | ||
1906 | * How long to wait before we try to reconnect to FS service? | ||
1907 | */ | ||
1908 | struct GNUNET_TIME_Relative reconnect_backoff; | ||
1909 | |||
1910 | /** | ||
1911 | * Desired level of anonymity. | ||
1912 | */ | ||
1913 | uint32_t anonymity; | ||
1914 | |||
1915 | /** | ||
1916 | * The depth of the file-tree. | ||
1917 | */ | ||
1918 | unsigned int treedepth; | ||
1919 | |||
1920 | /** | ||
1921 | * Options for the download. | ||
1922 | */ | ||
1923 | enum GNUNET_FS_DownloadOptions options; | ||
1924 | |||
1925 | /** | ||
1926 | * Flag set upon transitive completion (includes child downloads). | ||
1927 | * This flag is only set to #GNUNET_YES for directories where all | ||
1928 | * child-downloads have also completed (and signalled completion). | ||
1929 | */ | ||
1930 | int has_finished; | ||
1931 | |||
1932 | /** | ||
1933 | * Are we ready to issue requests (reconstructions are finished)? | ||
1934 | */ | ||
1935 | int issue_requests; | ||
1936 | }; | ||
1937 | |||
1938 | |||
1939 | #endif | ||
1940 | |||
1941 | /* end of fs_api.h */ | ||
diff --git a/src/fs/fs_directory.c b/src/fs/fs_directory.c deleted file mode 100644 index c693f9216..000000000 --- a/src/fs/fs_directory.c +++ /dev/null | |||
@@ -1,676 +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 | |||
37 | #include "gnunet_fs_service.h" | ||
38 | #include "fs_api.h" | ||
39 | |||
40 | /** | ||
41 | * String that is used to indicate that a file | ||
42 | * is a GNUnet directory. | ||
43 | */ | ||
44 | #define GNUNET_DIRECTORY_MAGIC "\211GND\r\n\032\n" | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Does the meta-data claim that this is a directory? | ||
49 | * Checks if the mime-type is that of a GNUnet directory. | ||
50 | * | ||
51 | * @return #GNUNET_YES if it is, #GNUNET_NO if it is not, #GNUNET_SYSERR if | ||
52 | * we have no mime-type information (treat as #GNUNET_NO) | ||
53 | */ | ||
54 | int | ||
55 | GNUNET_FS_meta_data_test_for_directory (const struct | ||
56 | GNUNET_FS_MetaData *md) | ||
57 | { | ||
58 | char *mime; | ||
59 | int ret; | ||
60 | |||
61 | if (NULL == md) | ||
62 | return GNUNET_SYSERR; | ||
63 | mime = GNUNET_FS_meta_data_get_by_type (md, | ||
64 | EXTRACTOR_METATYPE_MIMETYPE); | ||
65 | if (NULL == mime) | ||
66 | return GNUNET_SYSERR; | ||
67 | ret = (0 == strcasecmp (mime, GNUNET_FS_DIRECTORY_MIME)) ? GNUNET_YES : | ||
68 | GNUNET_NO; | ||
69 | GNUNET_free (mime); | ||
70 | return ret; | ||
71 | } | ||
72 | |||
73 | |||
74 | /** | ||
75 | * Set the MIMETYPE information for the given | ||
76 | * metadata to "application/gnunet-directory". | ||
77 | * | ||
78 | * @param md metadata to add mimetype to | ||
79 | */ | ||
80 | void | ||
81 | GNUNET_FS_meta_data_make_directory (struct GNUNET_FS_MetaData *md) | ||
82 | { | ||
83 | char *mime; | ||
84 | |||
85 | mime = | ||
86 | GNUNET_FS_meta_data_get_by_type (md, EXTRACTOR_METATYPE_MIMETYPE); | ||
87 | if (mime != NULL) | ||
88 | { | ||
89 | GNUNET_break (0 == strcmp (mime, GNUNET_FS_DIRECTORY_MIME)); | ||
90 | GNUNET_free (mime); | ||
91 | return; | ||
92 | } | ||
93 | GNUNET_FS_meta_data_insert (md, "<gnunet>", | ||
94 | EXTRACTOR_METATYPE_MIMETYPE, | ||
95 | EXTRACTOR_METAFORMAT_UTF8, "text/plain", | ||
96 | GNUNET_FS_DIRECTORY_MIME, | ||
97 | strlen (GNUNET_FS_DIRECTORY_MIME) + 1); | ||
98 | } | ||
99 | |||
100 | |||
101 | /** | ||
102 | * Closure for 'find_full_data'. | ||
103 | */ | ||
104 | struct GetFullDataClosure | ||
105 | { | ||
106 | /** | ||
107 | * Extracted binary meta data. | ||
108 | */ | ||
109 | void *data; | ||
110 | |||
111 | /** | ||
112 | * Number of bytes stored in data. | ||
113 | */ | ||
114 | size_t size; | ||
115 | }; | ||
116 | |||
117 | |||
118 | /** | ||
119 | * Type of a function that libextractor calls for each | ||
120 | * meta data item found. | ||
121 | * | ||
122 | * @param cls closure (user-defined) | ||
123 | * @param plugin_name name of the plugin that produced this value; | ||
124 | * special values can be used (e.g. '<zlib>' for zlib being | ||
125 | * used in the main libextractor library and yielding | ||
126 | * meta data). | ||
127 | * @param type libextractor-type describing the meta data | ||
128 | * @param format basic format information about data | ||
129 | * @param data_mime_type mime-type of data (not of the original file); | ||
130 | * can be NULL (if mime-type is not known) | ||
131 | * @param data actual meta-data found | ||
132 | * @param data_len number of bytes in data | ||
133 | * @return 0 to continue extracting, 1 to abort | ||
134 | */ | ||
135 | static int | ||
136 | find_full_data (void *cls, const char *plugin_name, | ||
137 | enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format, | ||
138 | const char *data_mime_type, const char *data, size_t data_len) | ||
139 | { | ||
140 | struct GetFullDataClosure *gfdc = cls; | ||
141 | |||
142 | if (type == EXTRACTOR_METATYPE_GNUNET_FULL_DATA) | ||
143 | { | ||
144 | gfdc->size = data_len; | ||
145 | if (data_len > 0) | ||
146 | { | ||
147 | gfdc->data = GNUNET_malloc (data_len); | ||
148 | GNUNET_memcpy (gfdc->data, data, data_len); | ||
149 | } | ||
150 | return 1; | ||
151 | } | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | |||
156 | /** | ||
157 | * Iterate over all entries in a directory. Note that directories | ||
158 | * are structured such that it is possible to iterate over the | ||
159 | * individual blocks as well as over the entire directory. Thus | ||
160 | * a client can call this function on the buffer in the | ||
161 | * GNUNET_FS_ProgressCallback. Also, directories can optionally | ||
162 | * include the contents of (small) files embedded in the directory | ||
163 | * itself; for those files, the processor may be given the | ||
164 | * contents of the file directly by this function. | ||
165 | * <p> | ||
166 | * | ||
167 | * Note that this function maybe called on parts of directories. Thus | ||
168 | * parser errors should not be reported _at all_ (with GNUNET_break). | ||
169 | * Still, if some entries can be recovered despite these parsing | ||
170 | * errors, the function should try to do this. | ||
171 | * | ||
172 | * @param size number of bytes in data | ||
173 | * @param data pointer to the beginning of the directory | ||
174 | * @param offset offset of data in the directory | ||
175 | * @param dep function to call on each entry | ||
176 | * @param dep_cls closure for @a dep | ||
177 | * @return #GNUNET_OK if this could be a block in a directory, | ||
178 | * #GNUNET_NO if this could be part of a directory (but not 100% OK) | ||
179 | * #GNUNET_SYSERR if @a data does not represent a directory | ||
180 | */ | ||
181 | int | ||
182 | GNUNET_FS_directory_list_contents (size_t size, | ||
183 | const void *data, | ||
184 | uint64_t offset, | ||
185 | GNUNET_FS_DirectoryEntryProcessor dep, | ||
186 | void *dep_cls) | ||
187 | { | ||
188 | struct GetFullDataClosure full_data; | ||
189 | const char *cdata = data; | ||
190 | char *emsg; | ||
191 | uint64_t pos; | ||
192 | uint64_t align; | ||
193 | uint32_t mdSize; | ||
194 | uint64_t epos; | ||
195 | struct GNUNET_FS_Uri *uri; | ||
196 | struct GNUNET_FS_MetaData *md; | ||
197 | char *filename; | ||
198 | |||
199 | if ((offset == 0) && | ||
200 | ((size < 8 + sizeof(uint32_t)) || | ||
201 | (0 != memcmp (cdata, | ||
202 | GNUNET_FS_DIRECTORY_MAGIC, | ||
203 | 8)))) | ||
204 | return GNUNET_SYSERR; | ||
205 | pos = offset; | ||
206 | if (offset == 0) | ||
207 | { | ||
208 | GNUNET_memcpy (&mdSize, | ||
209 | &cdata[8], | ||
210 | sizeof(uint32_t)); | ||
211 | mdSize = ntohl (mdSize); | ||
212 | if (mdSize > size - 8 - sizeof(uint32_t)) | ||
213 | { | ||
214 | /* invalid size */ | ||
215 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
216 | _ ("MAGIC mismatch. This is not a GNUnet directory.\n")); | ||
217 | return GNUNET_SYSERR; | ||
218 | } | ||
219 | md = GNUNET_FS_meta_data_deserialize (&cdata[8 + sizeof(uint32_t)], | ||
220 | mdSize); | ||
221 | if (md == NULL) | ||
222 | { | ||
223 | GNUNET_break (0); | ||
224 | return GNUNET_SYSERR; /* malformed ! */ | ||
225 | } | ||
226 | dep (dep_cls, | ||
227 | NULL, | ||
228 | NULL, | ||
229 | md, | ||
230 | 0, | ||
231 | NULL); | ||
232 | GNUNET_FS_meta_data_destroy (md); | ||
233 | pos = 8 + sizeof(uint32_t) + mdSize; | ||
234 | } | ||
235 | while (pos < size) | ||
236 | { | ||
237 | /* find end of URI */ | ||
238 | if (cdata[pos] == '\0') | ||
239 | { | ||
240 | /* URI is never empty, must be end of block, | ||
241 | * skip to next alignment */ | ||
242 | align = ((pos / DBLOCK_SIZE) + 1) * DBLOCK_SIZE; | ||
243 | if (align == pos) | ||
244 | { | ||
245 | /* if we were already aligned, still skip a block! */ | ||
246 | align += DBLOCK_SIZE; | ||
247 | } | ||
248 | pos = align; | ||
249 | if (pos >= size) | ||
250 | { | ||
251 | /* malformed - or partial download... */ | ||
252 | break; | ||
253 | } | ||
254 | } | ||
255 | epos = pos; | ||
256 | while ((epos < size) && (cdata[epos] != '\0')) | ||
257 | epos++; | ||
258 | if (epos >= size) | ||
259 | return GNUNET_NO; /* malformed - or partial download */ | ||
260 | |||
261 | uri = GNUNET_FS_uri_parse (&cdata[pos], &emsg); | ||
262 | pos = epos + 1; | ||
263 | if (NULL == uri) | ||
264 | { | ||
265 | GNUNET_free (emsg); | ||
266 | pos--; /* go back to '\0' to force going to next alignment */ | ||
267 | continue; | ||
268 | } | ||
269 | if (GNUNET_FS_uri_test_ksk (uri)) | ||
270 | { | ||
271 | GNUNET_FS_uri_destroy (uri); | ||
272 | GNUNET_break (0); | ||
273 | return GNUNET_NO; /* illegal in directory! */ | ||
274 | } | ||
275 | |||
276 | GNUNET_memcpy (&mdSize, | ||
277 | &cdata[pos], | ||
278 | sizeof(uint32_t)); | ||
279 | mdSize = ntohl (mdSize); | ||
280 | pos += sizeof(uint32_t); | ||
281 | if (pos + mdSize > size) | ||
282 | { | ||
283 | GNUNET_FS_uri_destroy (uri); | ||
284 | return GNUNET_NO; /* malformed - or partial download */ | ||
285 | } | ||
286 | |||
287 | md = GNUNET_FS_meta_data_deserialize (&cdata[pos], | ||
288 | mdSize); | ||
289 | if (NULL == md) | ||
290 | { | ||
291 | GNUNET_FS_uri_destroy (uri); | ||
292 | GNUNET_break (0); | ||
293 | return GNUNET_NO; /* malformed ! */ | ||
294 | } | ||
295 | pos += mdSize; | ||
296 | filename = | ||
297 | GNUNET_FS_meta_data_get_by_type (md, | ||
298 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); | ||
299 | full_data.size = 0; | ||
300 | full_data.data = NULL; | ||
301 | GNUNET_FS_meta_data_iterate (md, | ||
302 | &find_full_data, | ||
303 | &full_data); | ||
304 | if (NULL != dep) | ||
305 | { | ||
306 | dep (dep_cls, | ||
307 | filename, | ||
308 | uri, | ||
309 | md, | ||
310 | full_data.size, | ||
311 | full_data.data); | ||
312 | } | ||
313 | GNUNET_free (full_data.data); | ||
314 | GNUNET_free (filename); | ||
315 | GNUNET_FS_meta_data_destroy (md); | ||
316 | GNUNET_FS_uri_destroy (uri); | ||
317 | } | ||
318 | return GNUNET_OK; | ||
319 | } | ||
320 | |||
321 | |||
322 | /** | ||
323 | * Entries in the directory (builder). | ||
324 | */ | ||
325 | struct BuilderEntry | ||
326 | { | ||
327 | /** | ||
328 | * This is a linked list. | ||
329 | */ | ||
330 | struct BuilderEntry *next; | ||
331 | |||
332 | /** | ||
333 | * Length of this entry. | ||
334 | */ | ||
335 | size_t len; | ||
336 | }; | ||
337 | |||
338 | /** | ||
339 | * Internal state of a directory builder. | ||
340 | */ | ||
341 | struct GNUNET_FS_DirectoryBuilder | ||
342 | { | ||
343 | /** | ||
344 | * Meta-data for the directory itself. | ||
345 | */ | ||
346 | struct GNUNET_FS_MetaData *meta; | ||
347 | |||
348 | /** | ||
349 | * Head of linked list of entries. | ||
350 | */ | ||
351 | struct BuilderEntry *head; | ||
352 | |||
353 | /** | ||
354 | * Number of entries in the directory. | ||
355 | */ | ||
356 | unsigned int count; | ||
357 | }; | ||
358 | |||
359 | |||
360 | /** | ||
361 | * Create a directory builder. | ||
362 | * | ||
363 | * @param mdir metadata for the directory | ||
364 | */ | ||
365 | struct GNUNET_FS_DirectoryBuilder * | ||
366 | GNUNET_FS_directory_builder_create (const struct GNUNET_FS_MetaData | ||
367 | *mdir) | ||
368 | { | ||
369 | struct GNUNET_FS_DirectoryBuilder *ret; | ||
370 | |||
371 | ret = GNUNET_new (struct GNUNET_FS_DirectoryBuilder); | ||
372 | if (mdir != NULL) | ||
373 | ret->meta = GNUNET_FS_meta_data_duplicate (mdir); | ||
374 | else | ||
375 | ret->meta = GNUNET_FS_meta_data_create (); | ||
376 | GNUNET_FS_meta_data_make_directory (ret->meta); | ||
377 | return ret; | ||
378 | } | ||
379 | |||
380 | |||
381 | /** | ||
382 | * Add an entry to a directory. | ||
383 | * | ||
384 | * @param bld directory to extend | ||
385 | * @param uri uri of the entry (must not be a KSK) | ||
386 | * @param md metadata of the entry | ||
387 | * @param data raw data of the entry, can be NULL, otherwise | ||
388 | * data must point to exactly the number of bytes specified | ||
389 | * by the uri which must be of type LOC or CHK | ||
390 | */ | ||
391 | void | ||
392 | GNUNET_FS_directory_builder_add (struct GNUNET_FS_DirectoryBuilder *bld, | ||
393 | const struct GNUNET_FS_Uri *uri, | ||
394 | const struct GNUNET_FS_MetaData *md, | ||
395 | const void *data) | ||
396 | { | ||
397 | struct GNUNET_FS_Uri *curi; | ||
398 | struct BuilderEntry *e; | ||
399 | uint64_t fsize; | ||
400 | uint32_t big; | ||
401 | ssize_t ret; | ||
402 | size_t mds; | ||
403 | size_t mdxs; | ||
404 | char *uris; | ||
405 | char *serialized; | ||
406 | char *sptr; | ||
407 | size_t slen; | ||
408 | struct GNUNET_FS_MetaData *meta; | ||
409 | const struct GNUNET_FS_MetaData *meta_use; | ||
410 | |||
411 | GNUNET_assert (! GNUNET_FS_uri_test_ksk (uri)); | ||
412 | if (NULL != data) | ||
413 | { | ||
414 | GNUNET_assert (! GNUNET_FS_uri_test_sks (uri)); | ||
415 | if (GNUNET_FS_uri_test_chk (uri)) | ||
416 | { | ||
417 | fsize = GNUNET_FS_uri_chk_get_file_size (uri); | ||
418 | } | ||
419 | else | ||
420 | { | ||
421 | curi = GNUNET_FS_uri_loc_get_uri (uri); | ||
422 | GNUNET_assert (NULL != curi); | ||
423 | fsize = GNUNET_FS_uri_chk_get_file_size (curi); | ||
424 | GNUNET_FS_uri_destroy (curi); | ||
425 | } | ||
426 | } | ||
427 | else | ||
428 | { | ||
429 | fsize = 0; /* not given */ | ||
430 | } | ||
431 | if (fsize > MAX_INLINE_SIZE) | ||
432 | fsize = 0; /* too large */ | ||
433 | uris = GNUNET_FS_uri_to_string (uri); | ||
434 | slen = strlen (uris) + 1; | ||
435 | mds = GNUNET_FS_meta_data_get_serialized_size (md); | ||
436 | meta_use = md; | ||
437 | meta = NULL; | ||
438 | if (fsize > 0) | ||
439 | { | ||
440 | meta = GNUNET_FS_meta_data_duplicate (md); | ||
441 | GNUNET_FS_meta_data_insert (meta, "<gnunet>", | ||
442 | EXTRACTOR_METATYPE_GNUNET_FULL_DATA, | ||
443 | EXTRACTOR_METAFORMAT_BINARY, NULL, data, | ||
444 | fsize); | ||
445 | mdxs = GNUNET_FS_meta_data_get_serialized_size (meta); | ||
446 | if ((slen + sizeof(uint32_t) + mdxs - 1) / DBLOCK_SIZE == | ||
447 | (slen + sizeof(uint32_t) + mds - 1) / DBLOCK_SIZE) | ||
448 | { | ||
449 | /* adding full data would not cause us to cross | ||
450 | * additional blocks, so add it! */ | ||
451 | meta_use = meta; | ||
452 | mds = mdxs; | ||
453 | } | ||
454 | } | ||
455 | |||
456 | if (mds > GNUNET_MAX_MALLOC_CHECKED / 2) | ||
457 | mds = GNUNET_MAX_MALLOC_CHECKED / 2; | ||
458 | e = GNUNET_malloc (sizeof(struct BuilderEntry) + slen + mds | ||
459 | + sizeof(uint32_t)); | ||
460 | serialized = (char *) &e[1]; | ||
461 | GNUNET_memcpy (serialized, uris, slen); | ||
462 | GNUNET_free (uris); | ||
463 | sptr = &serialized[slen + sizeof(uint32_t)]; | ||
464 | ret = | ||
465 | GNUNET_FS_meta_data_serialize (meta_use, &sptr, mds, | ||
466 | GNUNET_FS_META_DATA_SERIALIZE_PART); | ||
467 | if (NULL != meta) | ||
468 | GNUNET_FS_meta_data_destroy (meta); | ||
469 | if (ret == -1) | ||
470 | mds = 0; | ||
471 | else | ||
472 | mds = ret; | ||
473 | big = htonl (mds); | ||
474 | GNUNET_memcpy (&serialized[slen], &big, sizeof(uint32_t)); | ||
475 | e->len = slen + sizeof(uint32_t) + mds; | ||
476 | e->next = bld->head; | ||
477 | bld->head = e; | ||
478 | bld->count++; | ||
479 | } | ||
480 | |||
481 | |||
482 | /** | ||
483 | * Given the start and end position of a block of | ||
484 | * data, return the end position of that data | ||
485 | * after alignment to the DBLOCK_SIZE. | ||
486 | */ | ||
487 | static size_t | ||
488 | do_align (size_t start_position, size_t end_position) | ||
489 | { | ||
490 | size_t align; | ||
491 | |||
492 | align = (end_position / DBLOCK_SIZE) * DBLOCK_SIZE; | ||
493 | if ((start_position < align) && (end_position > align)) | ||
494 | return align + end_position - start_position; | ||
495 | return end_position; | ||
496 | } | ||
497 | |||
498 | |||
499 | /** | ||
500 | * Compute a permutation of the blocks to | ||
501 | * minimize the cost of alignment. Greedy packer. | ||
502 | * | ||
503 | * @param start starting position for the first block | ||
504 | * @param count size of the two arrays | ||
505 | * @param sizes the sizes of the individual blocks | ||
506 | * @param perm the permutation of the blocks (updated) | ||
507 | */ | ||
508 | static void | ||
509 | block_align (size_t start, unsigned int count, const size_t *sizes, | ||
510 | unsigned int *perm) | ||
511 | { | ||
512 | unsigned int i; | ||
513 | unsigned int j; | ||
514 | unsigned int tmp; | ||
515 | unsigned int best; | ||
516 | ssize_t badness; | ||
517 | size_t cpos; | ||
518 | size_t cend; | ||
519 | ssize_t cbad; | ||
520 | unsigned int cval; | ||
521 | |||
522 | cpos = start; | ||
523 | for (i = 0; i < count; i++) | ||
524 | { | ||
525 | start = cpos; | ||
526 | badness = 0x7FFFFFFF; | ||
527 | best = -1; | ||
528 | for (j = i; j < count; j++) | ||
529 | { | ||
530 | cval = perm[j]; | ||
531 | cend = cpos + sizes[cval]; | ||
532 | if (cpos % DBLOCK_SIZE == 0) | ||
533 | { | ||
534 | /* prefer placing the largest blocks first */ | ||
535 | cbad = -(cend % DBLOCK_SIZE); | ||
536 | } | ||
537 | else | ||
538 | { | ||
539 | if (cpos / DBLOCK_SIZE == cend / DBLOCK_SIZE) | ||
540 | { | ||
541 | /* Data fits into the same block! Prefer small left-overs! */ | ||
542 | cbad = DBLOCK_SIZE - cend % DBLOCK_SIZE; | ||
543 | } | ||
544 | else | ||
545 | { | ||
546 | /* Would have to waste space to re-align, add big factor, this | ||
547 | * case is a real loss (proportional to space wasted)! */ | ||
548 | cbad = DBLOCK_SIZE * (DBLOCK_SIZE - cpos % DBLOCK_SIZE); | ||
549 | } | ||
550 | } | ||
551 | if (cbad < badness) | ||
552 | { | ||
553 | best = j; | ||
554 | badness = cbad; | ||
555 | } | ||
556 | } | ||
557 | GNUNET_assert (best != -1); | ||
558 | tmp = perm[i]; | ||
559 | perm[i] = perm[best]; | ||
560 | perm[best] = tmp; | ||
561 | cpos += sizes[perm[i]]; | ||
562 | cpos = do_align (start, cpos); | ||
563 | } | ||
564 | } | ||
565 | |||
566 | |||
567 | /** | ||
568 | * Finish building the directory. Frees the | ||
569 | * builder context and returns the directory | ||
570 | * in-memory. | ||
571 | * | ||
572 | * @param bld directory to finish | ||
573 | * @param rsize set to the number of bytes needed | ||
574 | * @param rdata set to the encoded directory | ||
575 | * @return #GNUNET_OK on success | ||
576 | */ | ||
577 | int | ||
578 | GNUNET_FS_directory_builder_finish (struct GNUNET_FS_DirectoryBuilder *bld, | ||
579 | size_t *rsize, | ||
580 | void **rdata) | ||
581 | { | ||
582 | char *data; | ||
583 | char *sptr; | ||
584 | size_t *sizes; | ||
585 | unsigned int *perm; | ||
586 | unsigned int i; | ||
587 | unsigned int j; | ||
588 | struct BuilderEntry *pos; | ||
589 | struct BuilderEntry **bes; | ||
590 | size_t size; | ||
591 | size_t psize; | ||
592 | size_t off; | ||
593 | ssize_t ret; | ||
594 | uint32_t big; | ||
595 | |||
596 | size = strlen (GNUNET_DIRECTORY_MAGIC) + sizeof(uint32_t); | ||
597 | size += GNUNET_FS_meta_data_get_serialized_size (bld->meta); | ||
598 | sizes = NULL; | ||
599 | perm = NULL; | ||
600 | bes = NULL; | ||
601 | if (0 < bld->count) | ||
602 | { | ||
603 | sizes = GNUNET_new_array (bld->count, | ||
604 | size_t); | ||
605 | perm = GNUNET_new_array (bld->count, | ||
606 | unsigned int); | ||
607 | bes = GNUNET_new_array (bld->count, | ||
608 | struct BuilderEntry *); | ||
609 | pos = bld->head; | ||
610 | for (i = 0; i < bld->count; i++) | ||
611 | { | ||
612 | perm[i] = i; | ||
613 | bes[i] = pos; | ||
614 | sizes[i] = pos->len; | ||
615 | pos = pos->next; | ||
616 | } | ||
617 | block_align (size, bld->count, sizes, perm); | ||
618 | /* compute final size with alignment */ | ||
619 | for (i = 0; i < bld->count; i++) | ||
620 | { | ||
621 | psize = size; | ||
622 | size += sizes[perm[i]]; | ||
623 | size = do_align (psize, size); | ||
624 | } | ||
625 | } | ||
626 | *rsize = size; | ||
627 | data = GNUNET_malloc_large (size); | ||
628 | if (data == NULL) | ||
629 | { | ||
630 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, | ||
631 | "malloc"); | ||
632 | *rsize = 0; | ||
633 | *rdata = NULL; | ||
634 | GNUNET_free (sizes); | ||
635 | GNUNET_free (perm); | ||
636 | GNUNET_free (bes); | ||
637 | return GNUNET_SYSERR; | ||
638 | } | ||
639 | *rdata = data; | ||
640 | GNUNET_memcpy (data, | ||
641 | GNUNET_DIRECTORY_MAGIC, | ||
642 | strlen (GNUNET_DIRECTORY_MAGIC)); | ||
643 | off = strlen (GNUNET_DIRECTORY_MAGIC); | ||
644 | |||
645 | sptr = &data[off + sizeof(uint32_t)]; | ||
646 | ret = | ||
647 | GNUNET_FS_meta_data_serialize (bld->meta, | ||
648 | &sptr, | ||
649 | size - off - sizeof(uint32_t), | ||
650 | GNUNET_FS_META_DATA_SERIALIZE_FULL); | ||
651 | GNUNET_assert (ret != -1); | ||
652 | big = htonl (ret); | ||
653 | GNUNET_memcpy (&data[off], | ||
654 | &big, | ||
655 | sizeof(uint32_t)); | ||
656 | off += sizeof(uint32_t) + ret; | ||
657 | for (j = 0; j < bld->count; j++) | ||
658 | { | ||
659 | i = perm[j]; | ||
660 | psize = off; | ||
661 | off += sizes[i]; | ||
662 | off = do_align (psize, off); | ||
663 | GNUNET_memcpy (&data[off - sizes[i]], &(bes[i])[1], sizes[i]); | ||
664 | GNUNET_free (bes[i]); | ||
665 | } | ||
666 | GNUNET_free (sizes); | ||
667 | GNUNET_free (perm); | ||
668 | GNUNET_free (bes); | ||
669 | GNUNET_assert (off == size); | ||
670 | GNUNET_FS_meta_data_destroy (bld->meta); | ||
671 | GNUNET_free (bld); | ||
672 | return GNUNET_OK; | ||
673 | } | ||
674 | |||
675 | |||
676 | /* end of fs_directory.c */ | ||
diff --git a/src/fs/fs_dirmetascan.c b/src/fs/fs_dirmetascan.c deleted file mode 100644 index 2379e29ce..000000000 --- a/src/fs/fs_dirmetascan.c +++ /dev/null | |||
@@ -1,496 +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 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | #include "gnunet_scheduler_lib.h" | ||
32 | #include <pthread.h> | ||
33 | |||
34 | |||
35 | /** | ||
36 | * An opaque structure a pointer to which is returned to the | ||
37 | * caller to be used to control the scanner. | ||
38 | */ | ||
39 | struct GNUNET_FS_DirScanner | ||
40 | { | ||
41 | /** | ||
42 | * Helper process. | ||
43 | */ | ||
44 | struct GNUNET_HELPER_Handle *helper; | ||
45 | |||
46 | /** | ||
47 | * Expanded filename (as given by the scan initiator). | ||
48 | * The scanner thread stores a copy here, and frees it when it finishes. | ||
49 | */ | ||
50 | char *filename_expanded; | ||
51 | |||
52 | /** | ||
53 | * Second argument to helper process. | ||
54 | */ | ||
55 | char *ex_arg; | ||
56 | |||
57 | /** | ||
58 | * The function that will be called every time there's a progress | ||
59 | * message. | ||
60 | */ | ||
61 | GNUNET_FS_DirScannerProgressCallback progress_callback; | ||
62 | |||
63 | /** | ||
64 | * A closure for progress_callback. | ||
65 | */ | ||
66 | void *progress_callback_cls; | ||
67 | |||
68 | /** | ||
69 | * After the scan is finished, it will contain a pointer to the | ||
70 | * top-level directory entry in the directory tree built by the | ||
71 | * scanner. | ||
72 | */ | ||
73 | struct GNUNET_FS_ShareTreeItem *toplevel; | ||
74 | |||
75 | /** | ||
76 | * Current position during processing. | ||
77 | */ | ||
78 | struct GNUNET_FS_ShareTreeItem *pos; | ||
79 | |||
80 | /** | ||
81 | * Task scheduled when we are done. | ||
82 | */ | ||
83 | struct GNUNET_SCHEDULER_Task *stop_task; | ||
84 | |||
85 | /** | ||
86 | * Arguments for helper. | ||
87 | */ | ||
88 | char *args[4]; | ||
89 | }; | ||
90 | |||
91 | |||
92 | /** | ||
93 | * Abort the scan. Must not be called from within the progress_callback | ||
94 | * function. | ||
95 | * | ||
96 | * @param ds directory scanner structure | ||
97 | */ | ||
98 | void | ||
99 | GNUNET_FS_directory_scan_abort (struct GNUNET_FS_DirScanner *ds) | ||
100 | { | ||
101 | /* terminate helper */ | ||
102 | if (NULL != ds->helper) | ||
103 | GNUNET_HELPER_stop (ds->helper, GNUNET_NO); | ||
104 | |||
105 | /* free resources */ | ||
106 | if (NULL != ds->toplevel) | ||
107 | GNUNET_FS_share_tree_free (ds->toplevel); | ||
108 | if (NULL != ds->stop_task) | ||
109 | GNUNET_SCHEDULER_cancel (ds->stop_task); | ||
110 | GNUNET_free (ds->ex_arg); | ||
111 | GNUNET_free (ds->filename_expanded); | ||
112 | GNUNET_free (ds); | ||
113 | } | ||
114 | |||
115 | |||
116 | struct GNUNET_FS_ShareTreeItem * | ||
117 | GNUNET_FS_directory_scan_get_result (struct GNUNET_FS_DirScanner *ds) | ||
118 | { | ||
119 | struct GNUNET_FS_ShareTreeItem *result; | ||
120 | |||
121 | /* check that we're actually done */ | ||
122 | GNUNET_assert (NULL == ds->helper); | ||
123 | /* preserve result */ | ||
124 | result = ds->toplevel; | ||
125 | ds->toplevel = NULL; | ||
126 | GNUNET_FS_directory_scan_abort (ds); | ||
127 | return result; | ||
128 | } | ||
129 | |||
130 | |||
131 | /** | ||
132 | * Move in the directory from the given position to the next file | ||
133 | * in DFS traversal. | ||
134 | * | ||
135 | * @param pos current position | ||
136 | * @return next file, NULL for none | ||
137 | */ | ||
138 | static struct GNUNET_FS_ShareTreeItem * | ||
139 | advance (struct GNUNET_FS_ShareTreeItem *pos) | ||
140 | { | ||
141 | int moved; | ||
142 | |||
143 | GNUNET_assert (NULL != pos); | ||
144 | moved = 0; /* must not terminate, even on file, otherwise "normal" */ | ||
145 | while ((pos->is_directory == GNUNET_YES) || (0 == moved)) | ||
146 | { | ||
147 | if ((moved != -1) && (NULL != pos->children_head)) | ||
148 | { | ||
149 | pos = pos->children_head; | ||
150 | moved = 1; /* can terminate if file */ | ||
151 | continue; | ||
152 | } | ||
153 | if (NULL != pos->next) | ||
154 | { | ||
155 | pos = pos->next; | ||
156 | moved = 1; /* can terminate if file */ | ||
157 | continue; | ||
158 | } | ||
159 | if (NULL != pos->parent) | ||
160 | { | ||
161 | pos = pos->parent; | ||
162 | moved = -1; /* force move to 'next' or 'parent' */ | ||
163 | continue; | ||
164 | } | ||
165 | /* no more options, end of traversal */ | ||
166 | return NULL; | ||
167 | } | ||
168 | return pos; | ||
169 | } | ||
170 | |||
171 | |||
172 | /** | ||
173 | * Add another child node to the tree. | ||
174 | * | ||
175 | * @param parent parent of the child, NULL for top level | ||
176 | * @param filename name of the file or directory | ||
177 | * @param is_directory GNUNET_YES for directories | ||
178 | * @return new entry that was just created | ||
179 | */ | ||
180 | static struct GNUNET_FS_ShareTreeItem * | ||
181 | expand_tree (struct GNUNET_FS_ShareTreeItem *parent, | ||
182 | const char *filename, | ||
183 | int is_directory) | ||
184 | { | ||
185 | struct GNUNET_FS_ShareTreeItem *chld; | ||
186 | size_t slen; | ||
187 | |||
188 | chld = GNUNET_new (struct GNUNET_FS_ShareTreeItem); | ||
189 | chld->parent = parent; | ||
190 | chld->filename = GNUNET_strdup (filename); | ||
191 | GNUNET_asprintf (&chld->short_filename, | ||
192 | "%s%s", | ||
193 | GNUNET_STRINGS_get_short_name (filename), | ||
194 | is_directory == GNUNET_YES ? "/" : ""); | ||
195 | /* make sure we do not end with '//' */ | ||
196 | slen = strlen (chld->short_filename); | ||
197 | if ((slen >= 2) && (chld->short_filename[slen - 1] == '/') && | ||
198 | (chld->short_filename[slen - 2] == '/')) | ||
199 | chld->short_filename[slen - 1] = '\0'; | ||
200 | chld->is_directory = is_directory; | ||
201 | if (NULL != parent) | ||
202 | GNUNET_CONTAINER_DLL_insert (parent->children_head, | ||
203 | parent->children_tail, | ||
204 | chld); | ||
205 | return chld; | ||
206 | } | ||
207 | |||
208 | |||
209 | /** | ||
210 | * Task run last to shut everything down. | ||
211 | * | ||
212 | * @param cls the 'struct GNUNET_FS_DirScanner' | ||
213 | */ | ||
214 | static void | ||
215 | finish_scan (void *cls) | ||
216 | { | ||
217 | struct GNUNET_FS_DirScanner *ds = cls; | ||
218 | |||
219 | ds->stop_task = NULL; | ||
220 | if (NULL != ds->helper) | ||
221 | { | ||
222 | GNUNET_HELPER_stop (ds->helper, GNUNET_NO); | ||
223 | ds->helper = NULL; | ||
224 | } | ||
225 | ds->progress_callback (ds->progress_callback_cls, | ||
226 | NULL, | ||
227 | GNUNET_SYSERR, | ||
228 | GNUNET_FS_DIRSCANNER_FINISHED); | ||
229 | } | ||
230 | |||
231 | |||
232 | /** | ||
233 | * Called every time there is data to read from the scanner. | ||
234 | * Calls the scanner progress handler. | ||
235 | * | ||
236 | * @param cls the closure (directory scanner object) | ||
237 | * @param msg message from the helper process | ||
238 | * @return #GNUNET_OK on success, | ||
239 | * #GNUNET_NO to stop further processing (no error) | ||
240 | * #GNUNET_SYSERR to stop further processing with error | ||
241 | */ | ||
242 | static int | ||
243 | process_helper_msgs (void *cls, const struct GNUNET_MessageHeader *msg) | ||
244 | { | ||
245 | struct GNUNET_FS_DirScanner *ds = cls; | ||
246 | const char *filename; | ||
247 | size_t left; | ||
248 | |||
249 | #if 0 | ||
250 | fprintf (stderr, | ||
251 | "DMS parses %u-byte message of type %u\n", | ||
252 | (unsigned int) ntohs (msg->size), | ||
253 | (unsigned int) ntohs (msg->type)); | ||
254 | #endif | ||
255 | left = ntohs (msg->size) - sizeof(struct GNUNET_MessageHeader); | ||
256 | filename = (const char *) &msg[1]; | ||
257 | switch (ntohs (msg->type)) | ||
258 | { | ||
259 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_FILE: | ||
260 | if (filename[left - 1] != '\0') | ||
261 | { | ||
262 | GNUNET_break (0); | ||
263 | break; | ||
264 | } | ||
265 | ds->progress_callback (ds->progress_callback_cls, | ||
266 | filename, | ||
267 | GNUNET_NO, | ||
268 | GNUNET_FS_DIRSCANNER_FILE_START); | ||
269 | if (NULL == ds->toplevel) | ||
270 | { | ||
271 | ds->toplevel = expand_tree (ds->pos, filename, GNUNET_NO); | ||
272 | } | ||
273 | else | ||
274 | { | ||
275 | GNUNET_assert (NULL != ds->pos); | ||
276 | (void) expand_tree (ds->pos, filename, GNUNET_NO); | ||
277 | } | ||
278 | return GNUNET_OK; | ||
279 | |||
280 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_DIRECTORY: | ||
281 | if (filename[left - 1] != '\0') | ||
282 | { | ||
283 | GNUNET_break (0); | ||
284 | break; | ||
285 | } | ||
286 | if (0 == strcmp ("..", filename)) | ||
287 | { | ||
288 | if (NULL == ds->pos) | ||
289 | { | ||
290 | GNUNET_break (0); | ||
291 | break; | ||
292 | } | ||
293 | ds->pos = ds->pos->parent; | ||
294 | return GNUNET_OK; | ||
295 | } | ||
296 | ds->progress_callback (ds->progress_callback_cls, | ||
297 | filename, | ||
298 | GNUNET_YES, | ||
299 | GNUNET_FS_DIRSCANNER_FILE_START); | ||
300 | ds->pos = expand_tree (ds->pos, filename, GNUNET_YES); | ||
301 | if (NULL == ds->toplevel) | ||
302 | ds->toplevel = ds->pos; | ||
303 | return GNUNET_OK; | ||
304 | |||
305 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR: | ||
306 | break; | ||
307 | |||
308 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_SKIP_FILE: | ||
309 | if ('\0' != filename[left - 1]) | ||
310 | break; | ||
311 | ds->progress_callback (ds->progress_callback_cls, | ||
312 | filename, | ||
313 | GNUNET_SYSERR, | ||
314 | GNUNET_FS_DIRSCANNER_FILE_IGNORED); | ||
315 | return GNUNET_OK; | ||
316 | |||
317 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_COUNTING_DONE: | ||
318 | if (0 != left) | ||
319 | { | ||
320 | GNUNET_break (0); | ||
321 | break; | ||
322 | } | ||
323 | if (NULL == ds->toplevel) | ||
324 | break; | ||
325 | ds->progress_callback (ds->progress_callback_cls, | ||
326 | NULL, | ||
327 | GNUNET_SYSERR, | ||
328 | GNUNET_FS_DIRSCANNER_ALL_COUNTED); | ||
329 | ds->pos = ds->toplevel; | ||
330 | if (GNUNET_YES == ds->pos->is_directory) | ||
331 | ds->pos = advance (ds->pos); | ||
332 | return GNUNET_OK; | ||
333 | |||
334 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_META_DATA: { | ||
335 | size_t nlen; | ||
336 | const char *end; | ||
337 | |||
338 | if (NULL == ds->pos) | ||
339 | { | ||
340 | GNUNET_break (0); | ||
341 | break; | ||
342 | } | ||
343 | end = memchr (filename, 0, left); | ||
344 | if (NULL == end) | ||
345 | { | ||
346 | GNUNET_break (0); | ||
347 | break; | ||
348 | } | ||
349 | end++; | ||
350 | nlen = end - filename; | ||
351 | left -= nlen; | ||
352 | if (0 != strcmp (filename, ds->pos->filename)) | ||
353 | { | ||
354 | GNUNET_break (0); | ||
355 | break; | ||
356 | } | ||
357 | ds->progress_callback (ds->progress_callback_cls, | ||
358 | filename, | ||
359 | GNUNET_YES, | ||
360 | GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED); | ||
361 | if (0 < left) | ||
362 | { | ||
363 | ds->pos->meta = GNUNET_FS_meta_data_deserialize (end, left); | ||
364 | if (NULL == ds->pos->meta) | ||
365 | { | ||
366 | GNUNET_break (0); | ||
367 | break; | ||
368 | } | ||
369 | /* having full filenames is too dangerous; always make sure we clean them up */ | ||
370 | GNUNET_FS_meta_data_delete (ds->pos->meta, | ||
371 | EXTRACTOR_METATYPE_FILENAME, | ||
372 | NULL, | ||
373 | 0); | ||
374 | /* instead, put in our 'safer' original filename */ | ||
375 | GNUNET_FS_meta_data_insert (ds->pos->meta, | ||
376 | "<libgnunetfs>", | ||
377 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME, | ||
378 | EXTRACTOR_METAFORMAT_UTF8, | ||
379 | "text/plain", | ||
380 | ds->pos->short_filename, | ||
381 | strlen (ds->pos->short_filename) | ||
382 | + 1); | ||
383 | } | ||
384 | ds->pos->ksk_uri = GNUNET_FS_uri_ksk_create_from_meta_data ( | ||
385 | ds->pos->meta); | ||
386 | ds->pos = advance (ds->pos); | ||
387 | return GNUNET_OK; | ||
388 | } | ||
389 | |||
390 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_FINISHED: | ||
391 | if (NULL != ds->pos) | ||
392 | { | ||
393 | GNUNET_break (0); | ||
394 | break; | ||
395 | } | ||
396 | if (0 != left) | ||
397 | { | ||
398 | GNUNET_break (0); | ||
399 | break; | ||
400 | } | ||
401 | if (NULL == ds->toplevel) | ||
402 | break; | ||
403 | ds->stop_task = GNUNET_SCHEDULER_add_now (&finish_scan, ds); | ||
404 | return GNUNET_OK; | ||
405 | |||
406 | default: | ||
407 | GNUNET_break (0); | ||
408 | break; | ||
409 | } | ||
410 | ds->progress_callback (ds->progress_callback_cls, | ||
411 | NULL, | ||
412 | GNUNET_SYSERR, | ||
413 | GNUNET_FS_DIRSCANNER_INTERNAL_ERROR); | ||
414 | return GNUNET_OK; | ||
415 | } | ||
416 | |||
417 | |||
418 | /** | ||
419 | * Function called if our helper process died. | ||
420 | * | ||
421 | * @param cls the 'struct GNUNET_FS_DirScanner' callback. | ||
422 | */ | ||
423 | static void | ||
424 | helper_died_cb (void *cls) | ||
425 | { | ||
426 | struct GNUNET_FS_DirScanner *ds = cls; | ||
427 | |||
428 | ds->helper = NULL; | ||
429 | if (NULL != ds->stop_task) | ||
430 | return; /* normal death, was finished */ | ||
431 | ds->progress_callback (ds->progress_callback_cls, | ||
432 | NULL, | ||
433 | GNUNET_SYSERR, | ||
434 | GNUNET_FS_DIRSCANNER_INTERNAL_ERROR); | ||
435 | } | ||
436 | |||
437 | |||
438 | /** | ||
439 | * Start a directory scanner thread. | ||
440 | * | ||
441 | * @param filename name of the directory to scan | ||
442 | * @param disable_extractor #GNUNET_YES to not run libextractor on files (only | ||
443 | * build a tree) | ||
444 | * @param ex if not NULL, must be a list of extra plugins for extractor | ||
445 | * @param cb the callback to call when there are scanning progress messages | ||
446 | * @param cb_cls closure for 'cb' | ||
447 | * @return directory scanner object to be used for controlling the scanner | ||
448 | */ | ||
449 | struct GNUNET_FS_DirScanner * | ||
450 | GNUNET_FS_directory_scan_start (const char *filename, | ||
451 | int disable_extractor, | ||
452 | const char *ex, | ||
453 | GNUNET_FS_DirScannerProgressCallback cb, | ||
454 | void *cb_cls) | ||
455 | { | ||
456 | struct stat sbuf; | ||
457 | char *filename_expanded; | ||
458 | struct GNUNET_FS_DirScanner *ds; | ||
459 | |||
460 | if (0 != stat (filename, &sbuf)) | ||
461 | return NULL; | ||
462 | filename_expanded = GNUNET_STRINGS_filename_expand (filename); | ||
463 | if (NULL == filename_expanded) | ||
464 | return NULL; | ||
465 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
466 | "Starting to scan directory `%s'\n", | ||
467 | filename_expanded); | ||
468 | ds = GNUNET_new (struct GNUNET_FS_DirScanner); | ||
469 | ds->progress_callback = cb; | ||
470 | ds->progress_callback_cls = cb_cls; | ||
471 | ds->filename_expanded = filename_expanded; | ||
472 | if (disable_extractor) | ||
473 | ds->ex_arg = GNUNET_strdup ("-"); | ||
474 | else | ||
475 | ds->ex_arg = (NULL != ex) ? GNUNET_strdup (ex) : NULL; | ||
476 | ds->args[0] = "gnunet-helper-fs-publish"; | ||
477 | ds->args[1] = ds->filename_expanded; | ||
478 | ds->args[2] = ds->ex_arg; | ||
479 | ds->args[3] = NULL; | ||
480 | ds->helper = GNUNET_HELPER_start (GNUNET_NO, | ||
481 | "gnunet-helper-fs-publish", | ||
482 | ds->args, | ||
483 | &process_helper_msgs, | ||
484 | &helper_died_cb, | ||
485 | ds); | ||
486 | if (NULL == ds->helper) | ||
487 | { | ||
488 | GNUNET_free (filename_expanded); | ||
489 | GNUNET_free (ds); | ||
490 | return NULL; | ||
491 | } | ||
492 | return ds; | ||
493 | } | ||
494 | |||
495 | |||
496 | /* end of fs_dirmetascan.c */ | ||
diff --git a/src/fs/fs_download.c b/src/fs/fs_download.c deleted file mode 100644 index 2a21e4810..000000000 --- a/src/fs/fs_download.c +++ /dev/null | |||
@@ -1,2339 +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 | |||
28 | #include "gnunet_fs_service.h" | ||
29 | #include "fs_api.h" | ||
30 | #include "fs_tree.h" | ||
31 | |||
32 | |||
33 | /** | ||
34 | * Determine if the given download (options and meta data) should cause | ||
35 | * use to try to do a recursive download. | ||
36 | */ | ||
37 | static int | ||
38 | is_recursive_download (struct GNUNET_FS_DownloadContext *dc) | ||
39 | { | ||
40 | return (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) && | ||
41 | ((GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) || | ||
42 | ((NULL == dc->meta) && | ||
43 | ((NULL == dc->filename) || | ||
44 | ((strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) && | ||
45 | (NULL != strstr (dc->filename + strlen (dc->filename) | ||
46 | - strlen (GNUNET_FS_DIRECTORY_EXT), | ||
47 | GNUNET_FS_DIRECTORY_EXT)))))); | ||
48 | } | ||
49 | |||
50 | |||
51 | /** | ||
52 | * We're storing the IBLOCKS after the DBLOCKS on disk (so that we | ||
53 | * only have to truncate the file once we're done). | ||
54 | * | ||
55 | * Given the offset of a block (with respect to the DBLOCKS) and its | ||
56 | * depth, return the offset where we would store this block in the | ||
57 | * file. | ||
58 | * | ||
59 | * @param fsize overall file size | ||
60 | * @param off offset of the block in the file | ||
61 | * @param depth depth of the block in the tree, 0 for DBLOCK | ||
62 | * @return off for DBLOCKS (depth == treedepth), | ||
63 | * otherwise an offset past the end | ||
64 | * of the file that does not overlap | ||
65 | * with the range for any other block | ||
66 | */ | ||
67 | static uint64_t | ||
68 | compute_disk_offset (uint64_t fsize, uint64_t off, unsigned int depth) | ||
69 | { | ||
70 | unsigned int i; | ||
71 | uint64_t lsize; /* what is the size of all IBlocks for depth "i"? */ | ||
72 | uint64_t loff; /* where do IBlocks for depth "i" start? */ | ||
73 | unsigned int ioff; /* which IBlock corresponds to "off" at depth "i"? */ | ||
74 | |||
75 | if (0 == depth) | ||
76 | return off; | ||
77 | /* first IBlocks start at the end of file, rounded up | ||
78 | * to full DBLOCK_SIZE */ | ||
79 | loff = ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * DBLOCK_SIZE; | ||
80 | lsize = | ||
81 | ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * sizeof(struct ContentHashKey); | ||
82 | GNUNET_assert (0 == (off % DBLOCK_SIZE)); | ||
83 | ioff = (off / DBLOCK_SIZE); | ||
84 | for (i = 1; i < depth; i++) | ||
85 | { | ||
86 | loff += lsize; | ||
87 | lsize = (lsize + CHK_PER_INODE - 1) / CHK_PER_INODE; | ||
88 | GNUNET_assert (lsize > 0); | ||
89 | GNUNET_assert (0 == (ioff % CHK_PER_INODE)); | ||
90 | ioff /= CHK_PER_INODE; | ||
91 | } | ||
92 | return loff + ioff * sizeof(struct ContentHashKey); | ||
93 | } | ||
94 | |||
95 | |||
96 | /** | ||
97 | * Fill in all of the generic fields for a download event and call the | ||
98 | * callback. | ||
99 | * | ||
100 | * @param pi structure to fill in | ||
101 | * @param dc overall download context | ||
102 | */ | ||
103 | void | ||
104 | GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi, | ||
105 | struct GNUNET_FS_DownloadContext *dc) | ||
106 | { | ||
107 | pi->value.download.dc = dc; | ||
108 | pi->value.download.cctx = dc->client_info; | ||
109 | pi->value.download.pctx = | ||
110 | (NULL == dc->parent) ? NULL : dc->parent->client_info; | ||
111 | pi->value.download.sctx = | ||
112 | (NULL == dc->search) ? NULL : dc->search->client_info; | ||
113 | pi->value.download.uri = dc->uri; | ||
114 | pi->value.download.filename = dc->filename; | ||
115 | pi->value.download.size = dc->length; | ||
116 | /* FIXME: Fix duration calculation to account for pauses */ | ||
117 | pi->value.download.duration = | ||
118 | GNUNET_TIME_absolute_get_duration (dc->start_time); | ||
119 | pi->value.download.completed = dc->completed; | ||
120 | pi->value.download.anonymity = dc->anonymity; | ||
121 | pi->value.download.eta = | ||
122 | GNUNET_TIME_calculate_eta (dc->start_time, dc->completed, dc->length); | ||
123 | pi->value.download.is_active = (NULL == dc->mq) ? GNUNET_NO : GNUNET_YES; | ||
124 | pi->fsh = dc->h; | ||
125 | if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE)) | ||
126 | dc->client_info = dc->h->upcb (dc->h->upcb_cls, pi); | ||
127 | else | ||
128 | dc->client_info = GNUNET_FS_search_probe_progress_ (NULL, pi); | ||
129 | } | ||
130 | |||
131 | |||
132 | /** | ||
133 | * Closure for iterator processing results. | ||
134 | */ | ||
135 | struct ProcessResultClosure | ||
136 | { | ||
137 | /** | ||
138 | * Hash of data. | ||
139 | */ | ||
140 | struct GNUNET_HashCode query; | ||
141 | |||
142 | /** | ||
143 | * Data found in P2P network. | ||
144 | */ | ||
145 | const void *data; | ||
146 | |||
147 | /** | ||
148 | * Our download context. | ||
149 | */ | ||
150 | struct GNUNET_FS_DownloadContext *dc; | ||
151 | |||
152 | /** | ||
153 | * When did we last transmit the request? | ||
154 | */ | ||
155 | struct GNUNET_TIME_Absolute last_transmission; | ||
156 | |||
157 | /** | ||
158 | * Number of bytes in data. | ||
159 | */ | ||
160 | size_t size; | ||
161 | |||
162 | /** | ||
163 | * Type of data. | ||
164 | */ | ||
165 | enum GNUNET_BLOCK_Type type; | ||
166 | |||
167 | /** | ||
168 | * Flag to indicate if this block should be stored on disk. | ||
169 | */ | ||
170 | int do_store; | ||
171 | |||
172 | /** | ||
173 | * how much respect did we offer to get this reply? | ||
174 | */ | ||
175 | uint32_t respect_offered; | ||
176 | |||
177 | /** | ||
178 | * how often did we transmit the query? | ||
179 | */ | ||
180 | uint32_t num_transmissions; | ||
181 | }; | ||
182 | |||
183 | |||
184 | /** | ||
185 | * Iterator over entries in the pending requests in the 'active' map for the | ||
186 | * reply that we just got. | ||
187 | * | ||
188 | * @param cls closure (our `struct ProcessResultClosure`) | ||
189 | * @param key query for the given value / request | ||
190 | * @param value value in the hash map (a `struct DownloadRequest`) | ||
191 | * @return #GNUNET_YES (we should continue to iterate); unless serious error | ||
192 | */ | ||
193 | static int | ||
194 | process_result_with_request (void *cls, | ||
195 | const struct GNUNET_HashCode *key, | ||
196 | void *value); | ||
197 | |||
198 | |||
199 | /** | ||
200 | * We've found a matching block without downloading it. | ||
201 | * Encrypt it and pass it to our "receive" function as | ||
202 | * if we had received it from the network. | ||
203 | * | ||
204 | * @param dc download in question | ||
205 | * @param chk request this relates to | ||
206 | * @param dr request details | ||
207 | * @param block plaintext data matching request | ||
208 | * @param len number of bytes in block | ||
209 | * @param do_store should we still store the block on disk? | ||
210 | * @return GNUNET_OK on success | ||
211 | */ | ||
212 | static int | ||
213 | encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc, | ||
214 | const struct ContentHashKey *chk, | ||
215 | struct DownloadRequest *dr, | ||
216 | const char *block, | ||
217 | size_t len, | ||
218 | int do_store) | ||
219 | { | ||
220 | struct ProcessResultClosure prc; | ||
221 | char enc[len]; | ||
222 | struct GNUNET_CRYPTO_SymmetricSessionKey sk; | ||
223 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
224 | struct GNUNET_HashCode query; | ||
225 | |||
226 | GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv); | ||
227 | if (-1 == GNUNET_CRYPTO_symmetric_encrypt (block, len, &sk, &iv, enc)) | ||
228 | { | ||
229 | GNUNET_break (0); | ||
230 | return GNUNET_SYSERR; | ||
231 | } | ||
232 | GNUNET_CRYPTO_hash (enc, len, &query); | ||
233 | if (0 != memcmp (&query, &chk->query, sizeof(struct GNUNET_HashCode))) | ||
234 | { | ||
235 | GNUNET_break_op (0); | ||
236 | return GNUNET_SYSERR; | ||
237 | } | ||
238 | GNUNET_log ( | ||
239 | GNUNET_ERROR_TYPE_DEBUG, | ||
240 | "Matching %u byte block for `%s' at offset %llu already present, no need for download!\n", | ||
241 | (unsigned int) len, | ||
242 | dc->filename, | ||
243 | (unsigned long long) dr->offset); | ||
244 | /* already got it! */ | ||
245 | prc.dc = dc; | ||
246 | prc.data = enc; | ||
247 | prc.size = len; | ||
248 | prc.type = (0 == dr->depth) ? GNUNET_BLOCK_TYPE_FS_DBLOCK | ||
249 | : GNUNET_BLOCK_TYPE_FS_IBLOCK; | ||
250 | prc.query = chk->query; | ||
251 | prc.do_store = do_store; | ||
252 | prc.last_transmission = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
253 | process_result_with_request (&prc, &chk->key, dr); | ||
254 | return GNUNET_OK; | ||
255 | } | ||
256 | |||
257 | |||
258 | /** | ||
259 | * We've lost our connection with the FS service. | ||
260 | * Re-establish it and re-transmit all of our | ||
261 | * pending requests. | ||
262 | * | ||
263 | * @param dc download context that is having trouble | ||
264 | */ | ||
265 | static void | ||
266 | try_reconnect (struct GNUNET_FS_DownloadContext *dc); | ||
267 | |||
268 | |||
269 | /** | ||
270 | * We found an entry in a directory. Check if the respective child | ||
271 | * already exists and if not create the respective child download. | ||
272 | * | ||
273 | * @param cls the parent download | ||
274 | * @param filename name of the file in the directory | ||
275 | * @param uri URI of the file (CHK or LOC) | ||
276 | * @param meta meta data of the file | ||
277 | * @param length number of bytes in data | ||
278 | * @param data contents of the file (or NULL if they were not inlined) | ||
279 | */ | ||
280 | static void | ||
281 | trigger_recursive_download (void *cls, | ||
282 | const char *filename, | ||
283 | const struct GNUNET_FS_Uri *uri, | ||
284 | const struct GNUNET_FS_MetaData *meta, | ||
285 | size_t length, | ||
286 | const void *data); | ||
287 | |||
288 | |||
289 | /** | ||
290 | * We're done downloading a directory. Open the file and | ||
291 | * trigger all of the (remaining) child downloads. | ||
292 | * | ||
293 | * @param dc context of download that just completed | ||
294 | */ | ||
295 | static void | ||
296 | full_recursive_download (struct GNUNET_FS_DownloadContext *dc) | ||
297 | { | ||
298 | size_t size; | ||
299 | uint64_t size64; | ||
300 | void *data; | ||
301 | struct GNUNET_DISK_FileHandle *h; | ||
302 | struct GNUNET_DISK_MapHandle *m; | ||
303 | |||
304 | size64 = GNUNET_FS_uri_chk_get_file_size (dc->uri); | ||
305 | size = (size_t) size64; | ||
306 | if (size64 != (uint64_t) size) | ||
307 | { | ||
308 | GNUNET_log ( | ||
309 | GNUNET_ERROR_TYPE_ERROR, | ||
310 | _ ( | ||
311 | "Recursive downloads of directories larger than 4 GB are not supported on 32-bit systems\n")); | ||
312 | return; | ||
313 | } | ||
314 | if (NULL != dc->filename) | ||
315 | { | ||
316 | h = GNUNET_DISK_file_open (dc->filename, | ||
317 | GNUNET_DISK_OPEN_READ, | ||
318 | GNUNET_DISK_PERM_NONE); | ||
319 | } | ||
320 | else | ||
321 | { | ||
322 | GNUNET_assert (NULL != dc->temp_filename); | ||
323 | h = GNUNET_DISK_file_open (dc->temp_filename, | ||
324 | GNUNET_DISK_OPEN_READ, | ||
325 | GNUNET_DISK_PERM_NONE); | ||
326 | } | ||
327 | if (NULL == h) | ||
328 | return; /* oops */ | ||
329 | data = GNUNET_DISK_file_map (h, &m, GNUNET_DISK_MAP_TYPE_READ, size); | ||
330 | if (NULL == data) | ||
331 | { | ||
332 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
333 | _ ("Directory too large for system address space\n")); | ||
334 | } | ||
335 | else | ||
336 | { | ||
337 | if (GNUNET_OK != | ||
338 | GNUNET_FS_directory_list_contents (size, | ||
339 | data, | ||
340 | 0, | ||
341 | &trigger_recursive_download, | ||
342 | dc)) | ||
343 | { | ||
344 | GNUNET_log ( | ||
345 | GNUNET_ERROR_TYPE_WARNING, | ||
346 | _ ( | ||
347 | "Failed to access full directory contents of `%s' for recursive download\n"), | ||
348 | dc->filename); | ||
349 | } | ||
350 | GNUNET_DISK_file_unmap (m); | ||
351 | } | ||
352 | GNUNET_DISK_file_close (h); | ||
353 | if (NULL == dc->filename) | ||
354 | { | ||
355 | if (0 != unlink (dc->temp_filename)) | ||
356 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
357 | "unlink", | ||
358 | dc->temp_filename); | ||
359 | GNUNET_free (dc->temp_filename); | ||
360 | dc->temp_filename = NULL; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | |||
365 | /** | ||
366 | * Check if all child-downloads have completed (or trigger them if | ||
367 | * necessary) and once we're completely done, signal completion (and | ||
368 | * possibly recurse to parent). This function MUST be called when the | ||
369 | * download of a file itself is done or when the download of a file is | ||
370 | * done and then later a direct child download has completed (and | ||
371 | * hence this download may complete itself). | ||
372 | * | ||
373 | * @param dc download to check for completion of children | ||
374 | */ | ||
375 | static void | ||
376 | check_completed (struct GNUNET_FS_DownloadContext *dc) | ||
377 | { | ||
378 | struct GNUNET_FS_ProgressInfo pi; | ||
379 | struct GNUNET_FS_DownloadContext *pos; | ||
380 | |||
381 | /* first, check if we need to download children */ | ||
382 | if (is_recursive_download (dc)) | ||
383 | full_recursive_download (dc); | ||
384 | /* then, check if children are done already */ | ||
385 | for (pos = dc->child_head; NULL != pos; pos = pos->next) | ||
386 | { | ||
387 | if ((NULL == pos->emsg) && (pos->completed < pos->length)) | ||
388 | return; /* not done yet */ | ||
389 | if ((NULL != pos->child_head) && (pos->has_finished != GNUNET_YES)) | ||
390 | return; /* not transitively done yet */ | ||
391 | } | ||
392 | /* All of our children are done, so mark this download done */ | ||
393 | dc->has_finished = GNUNET_YES; | ||
394 | if (NULL != dc->job_queue) | ||
395 | { | ||
396 | GNUNET_FS_dequeue_ (dc->job_queue); | ||
397 | dc->job_queue = NULL; | ||
398 | } | ||
399 | if (NULL != dc->task) | ||
400 | { | ||
401 | GNUNET_SCHEDULER_cancel (dc->task); | ||
402 | dc->task = NULL; | ||
403 | } | ||
404 | if (NULL != dc->rfh) | ||
405 | { | ||
406 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh)); | ||
407 | dc->rfh = NULL; | ||
408 | } | ||
409 | GNUNET_FS_download_sync_ (dc); | ||
410 | |||
411 | /* signal completion */ | ||
412 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED; | ||
413 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
414 | |||
415 | /* let parent know */ | ||
416 | if (NULL != dc->parent) | ||
417 | check_completed (dc->parent); | ||
418 | } | ||
419 | |||
420 | |||
421 | /** | ||
422 | * We got a block of plaintext data (from the meta data). | ||
423 | * Try it for upward reconstruction of the data. On success, | ||
424 | * the top-level block will move to state BRS_DOWNLOAD_UP. | ||
425 | * | ||
426 | * @param dc context for the download | ||
427 | * @param dr download request to match against | ||
428 | * @param data plaintext data, starting from the beginning of the file | ||
429 | * @param data_len number of bytes in data | ||
430 | */ | ||
431 | static void | ||
432 | try_match_block (struct GNUNET_FS_DownloadContext *dc, | ||
433 | struct DownloadRequest *dr, | ||
434 | const char *data, | ||
435 | size_t data_len) | ||
436 | { | ||
437 | struct GNUNET_FS_ProgressInfo pi; | ||
438 | unsigned int i; | ||
439 | char enc[DBLOCK_SIZE]; | ||
440 | struct ContentHashKey chks[CHK_PER_INODE]; | ||
441 | struct ContentHashKey in_chk; | ||
442 | struct GNUNET_CRYPTO_SymmetricSessionKey sk; | ||
443 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
444 | size_t dlen; | ||
445 | struct DownloadRequest *drc; | ||
446 | struct GNUNET_DISK_FileHandle *fh; | ||
447 | int complete; | ||
448 | const char *fn; | ||
449 | const char *odata; | ||
450 | size_t odata_len; | ||
451 | |||
452 | odata = data; | ||
453 | odata_len = data_len; | ||
454 | if (BRS_DOWNLOAD_UP == dr->state) | ||
455 | return; | ||
456 | if (dr->depth > 0) | ||
457 | { | ||
458 | if ((dc->offset > 0) || | ||
459 | (dc->length < GNUNET_ntohll (dc->uri->data.chk.file_length))) | ||
460 | { | ||
461 | /* NOTE: this test is not tight, but should suffice; the issue | ||
462 | here is that 'dr->num_children' may inherently only specify a | ||
463 | smaller range than what is in the original file; | ||
464 | thus, reconstruction of (some) inner blocks will fail. | ||
465 | FIXME: we might eventually want to write a tighter test to | ||
466 | maximize the circumstances under which we do succeed with | ||
467 | IBlock reconstruction. (need good tests though). */return; | ||
468 | } | ||
469 | complete = GNUNET_YES; | ||
470 | for (i = 0; i < dr->num_children; i++) | ||
471 | { | ||
472 | drc = dr->children[i]; | ||
473 | try_match_block (dc, drc, data, data_len); | ||
474 | if (drc->state != BRS_RECONSTRUCT_META_UP) | ||
475 | complete = GNUNET_NO; | ||
476 | else | ||
477 | chks[i] = drc->chk; | ||
478 | } | ||
479 | if (GNUNET_YES != complete) | ||
480 | return; | ||
481 | data = (const char *) chks; | ||
482 | dlen = dr->num_children * sizeof(struct ContentHashKey); | ||
483 | } | ||
484 | else | ||
485 | { | ||
486 | if (dr->offset > data_len) | ||
487 | return; /* oops */ | ||
488 | dlen = GNUNET_MIN (data_len - dr->offset, DBLOCK_SIZE); | ||
489 | } | ||
490 | GNUNET_CRYPTO_hash (&data[dr->offset], dlen, &in_chk.key); | ||
491 | GNUNET_CRYPTO_hash_to_aes_key (&in_chk.key, &sk, &iv); | ||
492 | if (-1 == | ||
493 | GNUNET_CRYPTO_symmetric_encrypt (&data[dr->offset], dlen, &sk, &iv, enc)) | ||
494 | { | ||
495 | GNUNET_break (0); | ||
496 | return; | ||
497 | } | ||
498 | GNUNET_CRYPTO_hash (enc, dlen, &in_chk.query); | ||
499 | switch (dr->state) | ||
500 | { | ||
501 | case BRS_INIT: | ||
502 | dr->chk = in_chk; | ||
503 | dr->state = BRS_RECONSTRUCT_META_UP; | ||
504 | break; | ||
505 | |||
506 | case BRS_CHK_SET: | ||
507 | if (0 != memcmp (&in_chk, &dr->chk, sizeof(struct ContentHashKey))) | ||
508 | { | ||
509 | /* other peer provided bogus meta data */ | ||
510 | GNUNET_break_op (0); | ||
511 | break; | ||
512 | } | ||
513 | /* write block to disk */ | ||
514 | fn = (NULL != dc->filename) ? dc->filename : dc->temp_filename; | ||
515 | if (NULL != fn) | ||
516 | { | ||
517 | fh = GNUNET_DISK_file_open (fn, | ||
518 | GNUNET_DISK_OPEN_READWRITE | ||
519 | | GNUNET_DISK_OPEN_CREATE | ||
520 | | GNUNET_DISK_OPEN_TRUNCATE, | ||
521 | GNUNET_DISK_PERM_USER_READ | ||
522 | | GNUNET_DISK_PERM_USER_WRITE | ||
523 | | GNUNET_DISK_PERM_GROUP_READ | ||
524 | | GNUNET_DISK_PERM_OTHER_READ); | ||
525 | if (NULL == fh) | ||
526 | { | ||
527 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn); | ||
528 | GNUNET_asprintf (&dc->emsg, | ||
529 | _ ("Failed to open file `%s' for writing"), | ||
530 | fn); | ||
531 | GNUNET_DISK_file_close (fh); | ||
532 | dr->state = BRS_ERROR; | ||
533 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR; | ||
534 | pi.value.download.specifics.error.message = dc->emsg; | ||
535 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
536 | return; | ||
537 | } | ||
538 | if (data_len != GNUNET_DISK_file_write (fh, odata, odata_len)) | ||
539 | { | ||
540 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "write", fn); | ||
541 | GNUNET_asprintf (&dc->emsg, | ||
542 | _ ("Failed to open file `%s' for writing"), | ||
543 | fn); | ||
544 | GNUNET_DISK_file_close (fh); | ||
545 | dr->state = BRS_ERROR; | ||
546 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR; | ||
547 | pi.value.download.specifics.error.message = dc->emsg; | ||
548 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
549 | return; | ||
550 | } | ||
551 | GNUNET_DISK_file_close (fh); | ||
552 | } | ||
553 | /* signal success */ | ||
554 | dr->state = BRS_DOWNLOAD_UP; | ||
555 | dc->completed = dc->length; | ||
556 | GNUNET_FS_download_sync_ (dc); | ||
557 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS; | ||
558 | pi.value.download.specifics.progress.data = data; | ||
559 | pi.value.download.specifics.progress.offset = 0; | ||
560 | pi.value.download.specifics.progress.data_len = dlen; | ||
561 | pi.value.download.specifics.progress.depth = 0; | ||
562 | pi.value.download.specifics.progress.respect_offered = 0; | ||
563 | pi.value.download.specifics.progress.block_download_duration = | ||
564 | GNUNET_TIME_UNIT_ZERO; | ||
565 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
566 | if ((NULL != dc->filename) && | ||
567 | (0 != truncate (dc->filename, | ||
568 | GNUNET_ntohll (dc->uri->data.chk.file_length)))) | ||
569 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
570 | "truncate", | ||
571 | dc->filename); | ||
572 | check_completed (dc); | ||
573 | break; | ||
574 | |||
575 | default: | ||
576 | /* how did we get here? */ | ||
577 | GNUNET_break (0); | ||
578 | break; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | |||
583 | /** | ||
584 | * Type of a function that libextractor calls for each | ||
585 | * meta data item found. If we find full data meta data, | ||
586 | * call 'try_match_block' on it. | ||
587 | * | ||
588 | * @param cls our 'struct GNUNET_FS_DownloadContext*' | ||
589 | * @param plugin_name name of the plugin that produced this value; | ||
590 | * special values can be used (e.g. '<zlib>' for zlib being | ||
591 | * used in the main libextractor library and yielding | ||
592 | * meta data). | ||
593 | * @param type libextractor-type describing the meta data | ||
594 | * @param format basic format information about data | ||
595 | * @param data_mime_type mime-type of data (not of the original file); | ||
596 | * can be NULL (if mime-type is not known) | ||
597 | * @param data actual meta-data found | ||
598 | * @param data_len number of bytes in data | ||
599 | * @return 0 to continue extracting, 1 to abort | ||
600 | */ | ||
601 | static int | ||
602 | match_full_data (void *cls, | ||
603 | const char *plugin_name, | ||
604 | enum EXTRACTOR_MetaType type, | ||
605 | enum EXTRACTOR_MetaFormat format, | ||
606 | const char *data_mime_type, | ||
607 | const char *data, | ||
608 | size_t data_len) | ||
609 | { | ||
610 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
611 | |||
612 | if (EXTRACTOR_METATYPE_GNUNET_FULL_DATA != type) | ||
613 | return 0; | ||
614 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
615 | "Found %u bytes of FD!\n", | ||
616 | (unsigned int) data_len); | ||
617 | if (GNUNET_FS_uri_chk_get_file_size (dc->uri) != data_len) | ||
618 | { | ||
619 | GNUNET_break_op (0); | ||
620 | return 1; /* bogus meta data */ | ||
621 | } | ||
622 | try_match_block (dc, dc->top_request, data, data_len); | ||
623 | return 1; | ||
624 | } | ||
625 | |||
626 | |||
627 | /** | ||
628 | * Set the state of the given download request to | ||
629 | * BRS_DOWNLOAD_UP and propagate it up the tree. | ||
630 | * | ||
631 | * @param dr download request that is done | ||
632 | */ | ||
633 | static void | ||
634 | propagate_up (struct DownloadRequest *dr) | ||
635 | { | ||
636 | unsigned int i; | ||
637 | |||
638 | do | ||
639 | { | ||
640 | dr->state = BRS_DOWNLOAD_UP; | ||
641 | dr = dr->parent; | ||
642 | if (NULL == dr) | ||
643 | break; | ||
644 | for (i = 0; i < dr->num_children; i++) | ||
645 | if (dr->children[i]->state != BRS_DOWNLOAD_UP) | ||
646 | break; | ||
647 | } | ||
648 | while (i == dr->num_children); | ||
649 | } | ||
650 | |||
651 | |||
652 | /** | ||
653 | * Try top-down reconstruction. Before, the given request node | ||
654 | * must have the state BRS_CHK_SET. Afterwards, more nodes may | ||
655 | * have that state or advanced to BRS_DOWNLOAD_DOWN or even | ||
656 | * BRS_DOWNLOAD_UP. It is also possible to get BRS_ERROR on the | ||
657 | * top level. | ||
658 | * | ||
659 | * @param dc overall download this block belongs to | ||
660 | * @param dr block to reconstruct | ||
661 | */ | ||
662 | static void | ||
663 | try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc, | ||
664 | struct DownloadRequest *dr) | ||
665 | { | ||
666 | uint64_t off; | ||
667 | char block[DBLOCK_SIZE]; | ||
668 | struct GNUNET_HashCode key; | ||
669 | uint64_t total; | ||
670 | size_t len; | ||
671 | unsigned int i; | ||
672 | struct DownloadRequest *drc; | ||
673 | uint64_t child_block_size; | ||
674 | const struct ContentHashKey *chks; | ||
675 | int up_done; | ||
676 | |||
677 | GNUNET_assert (NULL != dc->rfh); | ||
678 | GNUNET_assert (BRS_CHK_SET == dr->state); | ||
679 | total = GNUNET_FS_uri_chk_get_file_size (dc->uri); | ||
680 | GNUNET_assert (dr->depth < dc->treedepth); | ||
681 | len = GNUNET_FS_tree_calculate_block_size (total, dr->offset, dr->depth); | ||
682 | GNUNET_assert (len <= DBLOCK_SIZE); | ||
683 | off = compute_disk_offset (total, dr->offset, dr->depth); | ||
684 | if (dc->old_file_size < off + len) | ||
685 | return; /* failure */ | ||
686 | if (off != GNUNET_DISK_file_seek (dc->rfh, off, GNUNET_DISK_SEEK_SET)) | ||
687 | { | ||
688 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "seek", dc->filename); | ||
689 | return; /* failure */ | ||
690 | } | ||
691 | if (len != GNUNET_DISK_file_read (dc->rfh, block, len)) | ||
692 | { | ||
693 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", dc->filename); | ||
694 | return; /* failure */ | ||
695 | } | ||
696 | GNUNET_CRYPTO_hash (block, len, &key); | ||
697 | if (0 != memcmp (&key, &dr->chk.key, sizeof(struct GNUNET_HashCode))) | ||
698 | return; /* mismatch */ | ||
699 | if (GNUNET_OK != | ||
700 | encrypt_existing_match (dc, &dr->chk, dr, block, len, GNUNET_NO)) | ||
701 | { | ||
702 | /* hash matches but encrypted block does not, really bad */ | ||
703 | dr->state = BRS_ERROR; | ||
704 | /* propagate up */ | ||
705 | while (NULL != dr->parent) | ||
706 | { | ||
707 | dr = dr->parent; | ||
708 | dr->state = BRS_ERROR; | ||
709 | } | ||
710 | return; | ||
711 | } | ||
712 | /* block matches */ | ||
713 | dr->state = BRS_DOWNLOAD_DOWN; | ||
714 | |||
715 | /* set CHKs for children */ | ||
716 | up_done = GNUNET_YES; | ||
717 | chks = (const struct ContentHashKey *) block; | ||
718 | for (i = 0; i < dr->num_children; i++) | ||
719 | { | ||
720 | drc = dr->children[i]; | ||
721 | GNUNET_assert (drc->offset >= dr->offset); | ||
722 | child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth); | ||
723 | GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size); | ||
724 | if (BRS_INIT == drc->state) | ||
725 | { | ||
726 | drc->state = BRS_CHK_SET; | ||
727 | drc->chk = chks[drc->chk_idx]; | ||
728 | try_top_down_reconstruction (dc, drc); | ||
729 | } | ||
730 | if (BRS_DOWNLOAD_UP != drc->state) | ||
731 | up_done = GNUNET_NO; /* children not all done */ | ||
732 | } | ||
733 | if (GNUNET_YES == up_done) | ||
734 | propagate_up (dr); /* children all done (or no children...) */ | ||
735 | } | ||
736 | |||
737 | |||
738 | /** | ||
739 | * Add entries to the message queue. | ||
740 | * | ||
741 | * @param cls our download context | ||
742 | * @param key unused | ||
743 | * @param entry entry of type `struct DownloadRequest` | ||
744 | * @return #GNUNET_OK | ||
745 | */ | ||
746 | static int | ||
747 | retry_entry (void *cls, const struct GNUNET_HashCode *key, void *entry) | ||
748 | { | ||
749 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
750 | struct DownloadRequest *dr = entry; | ||
751 | struct SearchMessage *sm; | ||
752 | struct GNUNET_MQ_Envelope *env; | ||
753 | |||
754 | env = GNUNET_MQ_msg (sm, GNUNET_MESSAGE_TYPE_FS_START_SEARCH); | ||
755 | if (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY)) | ||
756 | sm->options = htonl (GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY); | ||
757 | else | ||
758 | sm->options = htonl (GNUNET_FS_SEARCH_OPTION_NONE); | ||
759 | if (0 == dr->depth) | ||
760 | sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK); | ||
761 | else | ||
762 | sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK); | ||
763 | sm->anonymity_level = htonl (dc->anonymity); | ||
764 | sm->target = dc->target; | ||
765 | sm->query = dr->chk.query; | ||
766 | GNUNET_MQ_send (dc->mq, env); | ||
767 | return GNUNET_OK; | ||
768 | } | ||
769 | |||
770 | |||
771 | /** | ||
772 | * Schedule the download of the specified block in the tree. | ||
773 | * | ||
774 | * @param dc overall download this block belongs to | ||
775 | * @param dr request to schedule | ||
776 | */ | ||
777 | static void | ||
778 | schedule_block_download (struct GNUNET_FS_DownloadContext *dc, | ||
779 | struct DownloadRequest *dr) | ||
780 | { | ||
781 | unsigned int i; | ||
782 | |||
783 | switch (dr->state) | ||
784 | { | ||
785 | case BRS_INIT: | ||
786 | GNUNET_assert (0); | ||
787 | break; | ||
788 | |||
789 | case BRS_RECONSTRUCT_DOWN: | ||
790 | GNUNET_assert (0); | ||
791 | break; | ||
792 | |||
793 | case BRS_RECONSTRUCT_META_UP: | ||
794 | GNUNET_assert (0); | ||
795 | break; | ||
796 | |||
797 | case BRS_RECONSTRUCT_UP: | ||
798 | GNUNET_assert (0); | ||
799 | break; | ||
800 | |||
801 | case BRS_CHK_SET: | ||
802 | /* normal case, start download */ | ||
803 | break; | ||
804 | |||
805 | case BRS_DOWNLOAD_DOWN: | ||
806 | for (i = 0; i < dr->num_children; i++) | ||
807 | schedule_block_download (dc, dr->children[i]); | ||
808 | return; | ||
809 | |||
810 | case BRS_DOWNLOAD_UP: | ||
811 | /* We're done! */ | ||
812 | return; | ||
813 | |||
814 | case BRS_ERROR: | ||
815 | GNUNET_break (0); | ||
816 | return; | ||
817 | } | ||
818 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
819 | "Scheduling download at offset %llu and depth %u for `%s'\n", | ||
820 | (unsigned long long) dr->offset, | ||
821 | dr->depth, | ||
822 | GNUNET_h2s (&dr->chk.query)); | ||
823 | if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains_value (dc->active, | ||
824 | &dr->chk.query, | ||
825 | dr)) | ||
826 | return; /* already active */ | ||
827 | GNUNET_CONTAINER_multihashmap_put (dc->active, | ||
828 | &dr->chk.query, | ||
829 | dr, | ||
830 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
831 | if (NULL == dc->mq) | ||
832 | return; /* download not active */ | ||
833 | retry_entry (dc, &dr->chk.query, dr); | ||
834 | } | ||
835 | |||
836 | |||
837 | #define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX | ||
838 | |||
839 | /** | ||
840 | * We found an entry in a directory. Check if the respective child | ||
841 | * already exists and if not create the respective child download. | ||
842 | * | ||
843 | * @param cls the parent download | ||
844 | * @param filename name of the file in the directory | ||
845 | * @param uri URI of the file (CHK or LOC) | ||
846 | * @param meta meta data of the file | ||
847 | * @param length number of bytes in data | ||
848 | * @param data contents of the file (or NULL if they were not inlined) | ||
849 | */ | ||
850 | static void | ||
851 | trigger_recursive_download (void *cls, | ||
852 | const char *filename, | ||
853 | const struct GNUNET_FS_Uri *uri, | ||
854 | const struct GNUNET_FS_MetaData *meta, | ||
855 | size_t length, | ||
856 | const void *data) | ||
857 | { | ||
858 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
859 | struct GNUNET_FS_DownloadContext *cpos; | ||
860 | char *temp_name; | ||
861 | char *fn; | ||
862 | char *us; | ||
863 | char *ext; | ||
864 | char *dn; | ||
865 | char *pos; | ||
866 | char *full_name; | ||
867 | char *sfn; | ||
868 | |||
869 | if (NULL == uri) | ||
870 | return; /* entry for the directory itself */ | ||
871 | cpos = dc->child_head; | ||
872 | while (NULL != cpos) | ||
873 | { | ||
874 | if ((GNUNET_FS_uri_test_equal (uri, cpos->uri)) || | ||
875 | ((NULL != filename) && (0 == strcmp (cpos->filename, filename)))) | ||
876 | break; | ||
877 | cpos = cpos->next; | ||
878 | } | ||
879 | if (NULL != cpos) | ||
880 | return; /* already exists */ | ||
881 | fn = NULL; | ||
882 | if (NULL == filename) | ||
883 | { | ||
884 | fn = GNUNET_FS_meta_data_suggest_filename (meta); | ||
885 | if (NULL == fn) | ||
886 | { | ||
887 | us = GNUNET_FS_uri_to_string (uri); | ||
888 | fn = GNUNET_strdup (&us[strlen (GNUNET_FS_URI_CHK_PREFIX)]); | ||
889 | GNUNET_free (us); | ||
890 | } | ||
891 | else if ('.' == fn[0]) | ||
892 | { | ||
893 | ext = fn; | ||
894 | us = GNUNET_FS_uri_to_string (uri); | ||
895 | GNUNET_asprintf (&fn, | ||
896 | "%s%s", | ||
897 | &us[strlen (GNUNET_FS_URI_CHK_PREFIX)], | ||
898 | ext); | ||
899 | GNUNET_free (ext); | ||
900 | GNUNET_free (us); | ||
901 | } | ||
902 | /* change '\' to '/' (this should have happened | ||
903 | * during insertion, but malicious peers may | ||
904 | * not have done this) */ | ||
905 | while (NULL != (pos = strstr (fn, "\\"))) | ||
906 | *pos = '/'; | ||
907 | /* remove '../' everywhere (again, well-behaved | ||
908 | * peers don't do this, but don't trust that | ||
909 | * we did not get something nasty) */ | ||
910 | while (NULL != (pos = strstr (fn, "../"))) | ||
911 | { | ||
912 | pos[0] = '_'; | ||
913 | pos[1] = '_'; | ||
914 | pos[2] = '_'; | ||
915 | } | ||
916 | filename = fn; | ||
917 | } | ||
918 | if (NULL == dc->filename) | ||
919 | { | ||
920 | full_name = NULL; | ||
921 | } | ||
922 | else | ||
923 | { | ||
924 | dn = GNUNET_strdup (dc->filename); | ||
925 | GNUNET_break ( | ||
926 | (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) && | ||
927 | (NULL != strstr (dn + strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT), | ||
928 | GNUNET_FS_DIRECTORY_EXT))); | ||
929 | sfn = GNUNET_strdup (filename); | ||
930 | while ((strlen (sfn) > 0) && ('/' == filename[strlen (sfn) - 1])) | ||
931 | sfn[strlen (sfn) - 1] = '\0'; | ||
932 | if ((strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) && | ||
933 | (NULL != strstr (dn + strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT), | ||
934 | GNUNET_FS_DIRECTORY_EXT))) | ||
935 | dn[strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT)] = '\0'; | ||
936 | if ((GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) && | ||
937 | ((strlen (filename) < strlen (GNUNET_FS_DIRECTORY_EXT)) || | ||
938 | (NULL == strstr (filename + strlen (filename) | ||
939 | - strlen (GNUNET_FS_DIRECTORY_EXT), | ||
940 | GNUNET_FS_DIRECTORY_EXT)))) | ||
941 | { | ||
942 | GNUNET_asprintf (&full_name, | ||
943 | "%s%s%s%s", | ||
944 | dn, | ||
945 | DIR_SEPARATOR_STR, | ||
946 | sfn, | ||
947 | GNUNET_FS_DIRECTORY_EXT); | ||
948 | } | ||
949 | else | ||
950 | { | ||
951 | GNUNET_asprintf (&full_name, "%s%s%s", dn, DIR_SEPARATOR_STR, sfn); | ||
952 | } | ||
953 | GNUNET_free (sfn); | ||
954 | GNUNET_free (dn); | ||
955 | } | ||
956 | if ((NULL != full_name) && | ||
957 | (GNUNET_OK != GNUNET_DISK_directory_create_for_file (full_name))) | ||
958 | { | ||
959 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
960 | _ ( | ||
961 | "Failed to create directory for recursive download of `%s'\n"), | ||
962 | full_name); | ||
963 | GNUNET_free (full_name); | ||
964 | GNUNET_free (fn); | ||
965 | return; | ||
966 | } | ||
967 | |||
968 | temp_name = NULL; | ||
969 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
970 | "Triggering recursive download of size %llu with %u bytes MD\n", | ||
971 | (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri), | ||
972 | (unsigned int) GNUNET_FS_meta_data_get_serialized_size ( | ||
973 | meta)); | ||
974 | GNUNET_FS_download_start (dc->h, | ||
975 | uri, | ||
976 | meta, | ||
977 | full_name, | ||
978 | temp_name, | ||
979 | 0, | ||
980 | GNUNET_FS_uri_chk_get_file_size (uri), | ||
981 | dc->anonymity, | ||
982 | dc->options, | ||
983 | NULL, | ||
984 | dc); | ||
985 | GNUNET_free (full_name); | ||
986 | GNUNET_free (temp_name); | ||
987 | GNUNET_free (fn); | ||
988 | } | ||
989 | |||
990 | |||
991 | /** | ||
992 | * (recursively) free download request structure | ||
993 | * | ||
994 | * @param dr request to free | ||
995 | */ | ||
996 | void | ||
997 | GNUNET_FS_free_download_request_ (struct DownloadRequest *dr) | ||
998 | { | ||
999 | if (NULL == dr) | ||
1000 | return; | ||
1001 | for (unsigned int i = 0; i < dr->num_children; i++) | ||
1002 | GNUNET_FS_free_download_request_ (dr->children[i]); | ||
1003 | GNUNET_free (dr->children); | ||
1004 | GNUNET_free (dr); | ||
1005 | } | ||
1006 | |||
1007 | |||
1008 | static int | ||
1009 | process_result_with_request (void *cls, | ||
1010 | const struct GNUNET_HashCode *key, | ||
1011 | void *value) | ||
1012 | { | ||
1013 | struct ProcessResultClosure *prc = cls; | ||
1014 | struct DownloadRequest *dr = value; | ||
1015 | struct GNUNET_FS_DownloadContext *dc = prc->dc; | ||
1016 | struct DownloadRequest *drc; | ||
1017 | struct GNUNET_DISK_FileHandle *fh = NULL; | ||
1018 | struct GNUNET_CRYPTO_SymmetricSessionKey skey; | ||
1019 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
1020 | char pt[prc->size]; | ||
1021 | struct GNUNET_FS_ProgressInfo pi; | ||
1022 | uint64_t off; | ||
1023 | size_t bs; | ||
1024 | size_t app; | ||
1025 | int i; | ||
1026 | struct ContentHashKey *chkarr; | ||
1027 | |||
1028 | GNUNET_log ( | ||
1029 | GNUNET_ERROR_TYPE_DEBUG, | ||
1030 | "Received %u byte block `%s' matching pending request at depth %u and offset %llu/%llu\n", | ||
1031 | (unsigned int) prc->size, | ||
1032 | GNUNET_h2s (key), | ||
1033 | dr->depth, | ||
1034 | (unsigned long long) dr->offset, | ||
1035 | (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length)); | ||
1036 | bs = GNUNET_FS_tree_calculate_block_size (GNUNET_ntohll ( | ||
1037 | dc->uri->data.chk.file_length), | ||
1038 | dr->offset, | ||
1039 | dr->depth); | ||
1040 | if (prc->size != bs) | ||
1041 | { | ||
1042 | GNUNET_asprintf ( | ||
1043 | &dc->emsg, | ||
1044 | _ ( | ||
1045 | "Internal error or bogus download URI (expected %lu bytes at depth %u and offset %llu/%llu, got %lu bytes)"), | ||
1046 | bs, | ||
1047 | dr->depth, | ||
1048 | (unsigned long long) dr->offset, | ||
1049 | (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length), | ||
1050 | prc->size); | ||
1051 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s\n", dc->emsg); | ||
1052 | while (NULL != dr->parent) | ||
1053 | { | ||
1054 | dr->state = BRS_ERROR; | ||
1055 | dr = dr->parent; | ||
1056 | } | ||
1057 | dr->state = BRS_ERROR; | ||
1058 | goto signal_error; | ||
1059 | } | ||
1060 | |||
1061 | (void) GNUNET_CONTAINER_multihashmap_remove (dc->active, &prc->query, dr); | ||
1062 | GNUNET_CRYPTO_hash_to_aes_key (&dr->chk.key, &skey, &iv); | ||
1063 | if (-1 == | ||
1064 | GNUNET_CRYPTO_symmetric_decrypt (prc->data, prc->size, &skey, &iv, pt)) | ||
1065 | { | ||
1066 | GNUNET_break (0); | ||
1067 | dc->emsg = GNUNET_strdup (_ ("internal error decrypting content")); | ||
1068 | goto signal_error; | ||
1069 | } | ||
1070 | off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length), | ||
1071 | dr->offset, | ||
1072 | dr->depth); | ||
1073 | /* save to disk */ | ||
1074 | if ((GNUNET_YES == prc->do_store) && | ||
1075 | ((NULL != dc->filename) || (is_recursive_download (dc))) && | ||
1076 | ((dr->depth == dc->treedepth) || | ||
1077 | (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES)))) | ||
1078 | { | ||
1079 | fh = GNUNET_DISK_file_open (NULL != dc->filename ? dc->filename | ||
1080 | : dc->temp_filename, | ||
1081 | GNUNET_DISK_OPEN_READWRITE | ||
1082 | | GNUNET_DISK_OPEN_CREATE, | ||
1083 | GNUNET_DISK_PERM_USER_READ | ||
1084 | | GNUNET_DISK_PERM_USER_WRITE | ||
1085 | | GNUNET_DISK_PERM_GROUP_READ | ||
1086 | | GNUNET_DISK_PERM_OTHER_READ); | ||
1087 | if (NULL == fh) | ||
1088 | { | ||
1089 | GNUNET_asprintf (&dc->emsg, | ||
1090 | _ ("Download failed: could not open file `%s': %s"), | ||
1091 | dc->filename, | ||
1092 | strerror (errno)); | ||
1093 | goto signal_error; | ||
1094 | } | ||
1095 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1096 | "Saving decrypted block to disk at offset %llu\n", | ||
1097 | (unsigned long long) off); | ||
1098 | if ((off != GNUNET_DISK_file_seek (fh, off, GNUNET_DISK_SEEK_SET))) | ||
1099 | { | ||
1100 | GNUNET_asprintf (&dc->emsg, | ||
1101 | _ ("Failed to seek to offset %llu in file `%s': %s"), | ||
1102 | (unsigned long long) off, | ||
1103 | dc->filename, | ||
1104 | strerror (errno)); | ||
1105 | goto signal_error; | ||
1106 | } | ||
1107 | if (prc->size != GNUNET_DISK_file_write (fh, pt, prc->size)) | ||
1108 | { | ||
1109 | GNUNET_asprintf ( | ||
1110 | &dc->emsg, | ||
1111 | _ ("Failed to write block of %u bytes at offset %llu in file `%s': %s"), | ||
1112 | (unsigned int) prc->size, | ||
1113 | (unsigned long long) off, | ||
1114 | dc->filename, | ||
1115 | strerror (errno)); | ||
1116 | goto signal_error; | ||
1117 | } | ||
1118 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh)); | ||
1119 | fh = NULL; | ||
1120 | } | ||
1121 | |||
1122 | if (0 == dr->depth) | ||
1123 | { | ||
1124 | /* DBLOCK, update progress and try recursion if applicable */ | ||
1125 | app = prc->size; | ||
1126 | if (dr->offset < dc->offset) | ||
1127 | { | ||
1128 | /* starting offset begins in the middle of pt, | ||
1129 | * do not count first bytes as progress */ | ||
1130 | GNUNET_assert (app > (dc->offset - dr->offset)); | ||
1131 | app -= (dc->offset - dr->offset); | ||
1132 | } | ||
1133 | if (dr->offset + prc->size > dc->offset + dc->length) | ||
1134 | { | ||
1135 | /* end of block is after relevant range, | ||
1136 | * do not count last bytes as progress */ | ||
1137 | GNUNET_assert (app > | ||
1138 | (dr->offset + prc->size) - (dc->offset + dc->length)); | ||
1139 | app -= (dr->offset + prc->size) - (dc->offset + dc->length); | ||
1140 | } | ||
1141 | dc->completed += app; | ||
1142 | |||
1143 | /* do recursive download if option is set and either meta data | ||
1144 | * says it is a directory or if no meta data is given AND filename | ||
1145 | * ends in '.gnd' (top-level case) */ | ||
1146 | if (is_recursive_download (dc)) | ||
1147 | GNUNET_FS_directory_list_contents (prc->size, | ||
1148 | pt, | ||
1149 | off, | ||
1150 | &trigger_recursive_download, | ||
1151 | dc); | ||
1152 | } | ||
1153 | GNUNET_assert (dc->completed <= dc->length); | ||
1154 | dr->state = BRS_DOWNLOAD_DOWN; | ||
1155 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS; | ||
1156 | pi.value.download.specifics.progress.data = pt; | ||
1157 | pi.value.download.specifics.progress.offset = dr->offset; | ||
1158 | pi.value.download.specifics.progress.data_len = prc->size; | ||
1159 | pi.value.download.specifics.progress.depth = dr->depth; | ||
1160 | pi.value.download.specifics.progress.respect_offered = prc->respect_offered; | ||
1161 | pi.value.download.specifics.progress.num_transmissions = | ||
1162 | prc->num_transmissions; | ||
1163 | if (prc->last_transmission.abs_value_us != | ||
1164 | GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) | ||
1165 | pi.value.download.specifics.progress.block_download_duration = | ||
1166 | GNUNET_TIME_absolute_get_duration (prc->last_transmission); | ||
1167 | else | ||
1168 | pi.value.download.specifics.progress.block_download_duration = | ||
1169 | GNUNET_TIME_UNIT_ZERO; /* found locally */ | ||
1170 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1171 | if (0 == dr->depth) | ||
1172 | propagate_up (dr); | ||
1173 | |||
1174 | if (dc->completed == dc->length) | ||
1175 | { | ||
1176 | /* download completed, signal */ | ||
1177 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1178 | "Download completed, truncating file to desired length %llu\n", | ||
1179 | (unsigned long long) GNUNET_ntohll ( | ||
1180 | dc->uri->data.chk.file_length)); | ||
1181 | /* truncate file to size (since we store IBlocks at the end) */ | ||
1182 | if (NULL != dc->filename) | ||
1183 | { | ||
1184 | if (0 != truncate (dc->filename, | ||
1185 | GNUNET_ntohll (dc->uri->data.chk.file_length))) | ||
1186 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
1187 | "truncate", | ||
1188 | dc->filename); | ||
1189 | } | ||
1190 | GNUNET_assert (0 == dr->depth); | ||
1191 | check_completed (dc); | ||
1192 | } | ||
1193 | if (0 == dr->depth) | ||
1194 | { | ||
1195 | /* bottom of the tree, no child downloads possible, just sync */ | ||
1196 | GNUNET_FS_download_sync_ (dc); | ||
1197 | return GNUNET_YES; | ||
1198 | } | ||
1199 | |||
1200 | GNUNET_log ( | ||
1201 | GNUNET_ERROR_TYPE_DEBUG, | ||
1202 | "Triggering downloads of children (this block was at depth %u and offset %llu)\n", | ||
1203 | dr->depth, | ||
1204 | (unsigned long long) dr->offset); | ||
1205 | GNUNET_assert (0 == (prc->size % sizeof(struct ContentHashKey))); | ||
1206 | chkarr = (struct ContentHashKey *) pt; | ||
1207 | for (i = dr->num_children - 1; i >= 0; i--) | ||
1208 | { | ||
1209 | drc = dr->children[i]; | ||
1210 | switch (drc->state) | ||
1211 | { | ||
1212 | case BRS_INIT: | ||
1213 | if ((drc->chk_idx + 1) * sizeof(struct ContentHashKey) > prc->size) | ||
1214 | { | ||
1215 | /* 'chkarr' does not have enough space for this chk_idx; | ||
1216 | internal error! */ | ||
1217 | GNUNET_break (0); | ||
1218 | GNUNET_assert (0); | ||
1219 | dc->emsg = GNUNET_strdup (_ ("internal error decoding tree")); | ||
1220 | goto signal_error; | ||
1221 | } | ||
1222 | drc->chk = chkarr[drc->chk_idx]; | ||
1223 | drc->state = BRS_CHK_SET; | ||
1224 | if (GNUNET_YES == dc->issue_requests) | ||
1225 | schedule_block_download (dc, drc); | ||
1226 | break; | ||
1227 | |||
1228 | case BRS_RECONSTRUCT_DOWN: | ||
1229 | GNUNET_assert (0); | ||
1230 | break; | ||
1231 | |||
1232 | case BRS_RECONSTRUCT_META_UP: | ||
1233 | GNUNET_assert (0); | ||
1234 | break; | ||
1235 | |||
1236 | case BRS_RECONSTRUCT_UP: | ||
1237 | GNUNET_assert (0); | ||
1238 | break; | ||
1239 | |||
1240 | case BRS_CHK_SET: | ||
1241 | GNUNET_assert (0); | ||
1242 | break; | ||
1243 | |||
1244 | case BRS_DOWNLOAD_DOWN: | ||
1245 | GNUNET_assert (0); | ||
1246 | break; | ||
1247 | |||
1248 | case BRS_DOWNLOAD_UP: | ||
1249 | GNUNET_assert (0); | ||
1250 | break; | ||
1251 | |||
1252 | case BRS_ERROR: | ||
1253 | GNUNET_assert (0); | ||
1254 | break; | ||
1255 | |||
1256 | default: | ||
1257 | GNUNET_assert (0); | ||
1258 | break; | ||
1259 | } | ||
1260 | } | ||
1261 | GNUNET_FS_download_sync_ (dc); | ||
1262 | return GNUNET_YES; | ||
1263 | |||
1264 | signal_error: | ||
1265 | if (NULL != fh) | ||
1266 | GNUNET_DISK_file_close (fh); | ||
1267 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR; | ||
1268 | pi.value.download.specifics.error.message = dc->emsg; | ||
1269 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1270 | GNUNET_MQ_destroy (dc->mq); | ||
1271 | dc->mq = NULL; | ||
1272 | GNUNET_FS_free_download_request_ (dc->top_request); | ||
1273 | dc->top_request = NULL; | ||
1274 | if (NULL != dc->job_queue) | ||
1275 | { | ||
1276 | GNUNET_FS_dequeue_ (dc->job_queue); | ||
1277 | dc->job_queue = NULL; | ||
1278 | } | ||
1279 | GNUNET_FS_download_sync_ (dc); | ||
1280 | return GNUNET_NO; | ||
1281 | } | ||
1282 | |||
1283 | |||
1284 | /** | ||
1285 | * Type of a function to call when we check the PUT message | ||
1286 | * from the service. | ||
1287 | * | ||
1288 | * @param cls closure | ||
1289 | * @param msg message received | ||
1290 | */ | ||
1291 | static int | ||
1292 | check_put (void *cls, const struct ClientPutMessage *cm) | ||
1293 | { | ||
1294 | /* any varsize length is OK */ | ||
1295 | return GNUNET_OK; | ||
1296 | } | ||
1297 | |||
1298 | |||
1299 | /** | ||
1300 | * Type of a function to call when we receive a message | ||
1301 | * from the service. | ||
1302 | * | ||
1303 | * @param cls closure | ||
1304 | * @param msg message received | ||
1305 | */ | ||
1306 | static void | ||
1307 | handle_put (void *cls, const struct ClientPutMessage *cm) | ||
1308 | { | ||
1309 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1310 | uint16_t msize = ntohs (cm->header.size) - sizeof(*cm); | ||
1311 | struct ProcessResultClosure prc; | ||
1312 | |||
1313 | prc.dc = dc; | ||
1314 | prc.data = &cm[1]; | ||
1315 | prc.last_transmission = GNUNET_TIME_absolute_ntoh (cm->last_transmission); | ||
1316 | prc.size = msize; | ||
1317 | prc.type = ntohl (cm->type); | ||
1318 | prc.do_store = GNUNET_YES; | ||
1319 | prc.respect_offered = ntohl (cm->respect_offered); | ||
1320 | prc.num_transmissions = ntohl (cm->num_transmissions); | ||
1321 | GNUNET_CRYPTO_hash (prc.data, msize, &prc.query); | ||
1322 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1323 | "Received result for query `%s' from FS service\n", | ||
1324 | GNUNET_h2s (&prc.query)); | ||
1325 | GNUNET_CONTAINER_multihashmap_get_multiple (dc->active, | ||
1326 | &prc.query, | ||
1327 | &process_result_with_request, | ||
1328 | &prc); | ||
1329 | } | ||
1330 | |||
1331 | |||
1332 | /** | ||
1333 | * Generic error handler, called with the appropriate error code and | ||
1334 | * the same closure specified at the creation of the message queue. | ||
1335 | * Not every message queue implementation supports an error handler. | ||
1336 | * | ||
1337 | * @param cls closure with the `struct GNUNET_FS_DownloadContext *` | ||
1338 | * @param error error code | ||
1339 | */ | ||
1340 | static void | ||
1341 | download_mq_error_handler (void *cls, enum GNUNET_MQ_Error error) | ||
1342 | { | ||
1343 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1344 | |||
1345 | if (NULL != dc->mq) | ||
1346 | { | ||
1347 | GNUNET_MQ_destroy (dc->mq); | ||
1348 | dc->mq = NULL; | ||
1349 | } | ||
1350 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1351 | "Transmitting download request failed, trying to reconnect\n"); | ||
1352 | try_reconnect (dc); | ||
1353 | } | ||
1354 | |||
1355 | |||
1356 | /** | ||
1357 | * Reconnect to the FS service and transmit our queries NOW. | ||
1358 | * | ||
1359 | * @param cls our download context | ||
1360 | */ | ||
1361 | static void | ||
1362 | do_reconnect (void *cls) | ||
1363 | { | ||
1364 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1365 | struct GNUNET_MQ_MessageHandler handlers[] = | ||
1366 | { GNUNET_MQ_hd_var_size (put, | ||
1367 | GNUNET_MESSAGE_TYPE_FS_PUT, | ||
1368 | struct ClientPutMessage, | ||
1369 | dc), | ||
1370 | GNUNET_MQ_handler_end () }; | ||
1371 | |||
1372 | dc->task = NULL; | ||
1373 | dc->mq = GNUNET_CLIENT_connect (dc->h->cfg, | ||
1374 | "fs", | ||
1375 | handlers, | ||
1376 | &download_mq_error_handler, | ||
1377 | dc); | ||
1378 | if (NULL == dc->mq) | ||
1379 | { | ||
1380 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1381 | "Connecting to `%s'-service failed, will try again.\n", | ||
1382 | "FS"); | ||
1383 | try_reconnect (dc); | ||
1384 | return; | ||
1385 | } | ||
1386 | GNUNET_CONTAINER_multihashmap_iterate (dc->active, &retry_entry, dc); | ||
1387 | } | ||
1388 | |||
1389 | |||
1390 | /** | ||
1391 | * We've lost our connection with the FS service. | ||
1392 | * Re-establish it and re-transmit all of our | ||
1393 | * pending requests. | ||
1394 | * | ||
1395 | * @param dc download context that is having trouble | ||
1396 | */ | ||
1397 | static void | ||
1398 | try_reconnect (struct GNUNET_FS_DownloadContext *dc) | ||
1399 | { | ||
1400 | if (NULL != dc->mq) | ||
1401 | { | ||
1402 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1403 | "Moving all requests back to pending list\n"); | ||
1404 | GNUNET_MQ_destroy (dc->mq); | ||
1405 | dc->mq = NULL; | ||
1406 | } | ||
1407 | if (0 == dc->reconnect_backoff.rel_value_us) | ||
1408 | dc->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS; | ||
1409 | else | ||
1410 | dc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (dc->reconnect_backoff); | ||
1411 | |||
1412 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1413 | "Will try to reconnect in %s\n", | ||
1414 | GNUNET_STRINGS_relative_time_to_string (dc->reconnect_backoff, | ||
1415 | GNUNET_YES)); | ||
1416 | GNUNET_break (NULL != dc->job_queue); | ||
1417 | dc->task = | ||
1418 | GNUNET_SCHEDULER_add_delayed (dc->reconnect_backoff, &do_reconnect, dc); | ||
1419 | } | ||
1420 | |||
1421 | |||
1422 | /** | ||
1423 | * We're allowed to ask the FS service for our blocks. Start the download. | ||
1424 | * | ||
1425 | * @param cls the 'struct GNUNET_FS_DownloadContext' | ||
1426 | */ | ||
1427 | static void | ||
1428 | activate_fs_download (void *cls) | ||
1429 | { | ||
1430 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1431 | struct GNUNET_FS_ProgressInfo pi; | ||
1432 | |||
1433 | GNUNET_assert (NULL == dc->mq); | ||
1434 | GNUNET_assert (NULL != dc->active); | ||
1435 | do_reconnect (dc); | ||
1436 | if (NULL != dc->mq) | ||
1437 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download activated\n"); | ||
1438 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE; | ||
1439 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1440 | } | ||
1441 | |||
1442 | |||
1443 | /** | ||
1444 | * We must stop to ask the FS service for our blocks. Pause the download. | ||
1445 | * | ||
1446 | * @param cls the `struct GNUNET_FS_DownloadContext` | ||
1447 | */ | ||
1448 | static void | ||
1449 | deactivate_fs_download (void *cls) | ||
1450 | { | ||
1451 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1452 | struct GNUNET_FS_ProgressInfo pi; | ||
1453 | |||
1454 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download deactivated\n"); | ||
1455 | if (NULL != dc->mq) | ||
1456 | { | ||
1457 | GNUNET_MQ_destroy (dc->mq); | ||
1458 | dc->mq = NULL; | ||
1459 | } | ||
1460 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE; | ||
1461 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1462 | } | ||
1463 | |||
1464 | |||
1465 | /** | ||
1466 | * (recursively) Create a download request structure. | ||
1467 | * | ||
1468 | * @param parent parent of the current entry | ||
1469 | * @param chk_idx index of the chk for this block in the parent block | ||
1470 | * @param depth depth of the current entry, 0 are the DBLOCKs, | ||
1471 | * top level block is 'dc->treedepth - 1' | ||
1472 | * @param dr_offset offset in the original file this block maps to | ||
1473 | * (as in, offset of the first byte of the first DBLOCK | ||
1474 | * in the subtree rooted in the returned download request tree) | ||
1475 | * @param file_start_offset desired starting offset for the download | ||
1476 | * in the original file; requesting tree should not contain | ||
1477 | * DBLOCKs prior to the file_start_offset | ||
1478 | * @param desired_length desired number of bytes the user wanted to access | ||
1479 | * (from file_start_offset). Resulting tree should not contain | ||
1480 | * DBLOCKs after file_start_offset + file_length. | ||
1481 | * @return download request tree for the given range of DBLOCKs at | ||
1482 | * the specified depth | ||
1483 | */ | ||
1484 | static struct DownloadRequest * | ||
1485 | create_download_request (struct DownloadRequest *parent, | ||
1486 | unsigned int chk_idx, | ||
1487 | unsigned int depth, | ||
1488 | uint64_t dr_offset, | ||
1489 | uint64_t file_start_offset, | ||
1490 | uint64_t desired_length) | ||
1491 | { | ||
1492 | struct DownloadRequest *dr; | ||
1493 | unsigned int i; | ||
1494 | unsigned int head_skip; | ||
1495 | uint64_t child_block_size; | ||
1496 | |||
1497 | dr = GNUNET_new (struct DownloadRequest); | ||
1498 | dr->parent = parent; | ||
1499 | dr->depth = depth; | ||
1500 | dr->offset = dr_offset; | ||
1501 | dr->chk_idx = chk_idx; | ||
1502 | if (0 == depth) | ||
1503 | return dr; | ||
1504 | child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1); | ||
1505 | |||
1506 | /* calculate how many blocks at this level are not interesting | ||
1507 | * from the start (rounded down), either because of the requested | ||
1508 | * file offset or because this IBlock is further along */ | ||
1509 | if (dr_offset < file_start_offset) | ||
1510 | { | ||
1511 | head_skip = (file_start_offset - dr_offset) / child_block_size; | ||
1512 | } | ||
1513 | else | ||
1514 | { | ||
1515 | head_skip = 0; | ||
1516 | } | ||
1517 | |||
1518 | /* calculate index of last block at this level that is interesting (rounded up) */ | ||
1519 | dr->num_children = | ||
1520 | (file_start_offset + desired_length - dr_offset) / child_block_size; | ||
1521 | if (dr->num_children * child_block_size < | ||
1522 | file_start_offset + desired_length - dr_offset) | ||
1523 | dr->num_children++; /* round up */ | ||
1524 | GNUNET_assert (dr->num_children > head_skip); | ||
1525 | dr->num_children -= head_skip; | ||
1526 | if (dr->num_children > CHK_PER_INODE) | ||
1527 | dr->num_children = CHK_PER_INODE; /* cap at max */ | ||
1528 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1529 | "Block at offset %llu and depth %u has %u children\n", | ||
1530 | (unsigned long long) dr_offset, | ||
1531 | depth, | ||
1532 | dr->num_children); | ||
1533 | |||
1534 | /* now we can get the total number of *interesting* children for this block */ | ||
1535 | |||
1536 | /* why else would we have gotten here to begin with? (that'd be a bad logic error) */ | ||
1537 | GNUNET_assert (dr->num_children > 0); | ||
1538 | |||
1539 | dr->children = GNUNET_new_array (dr->num_children, struct DownloadRequest *); | ||
1540 | for (i = 0; i < dr->num_children; i++) | ||
1541 | { | ||
1542 | dr->children[i] = | ||
1543 | create_download_request (dr, | ||
1544 | i + head_skip, | ||
1545 | depth - 1, | ||
1546 | dr_offset + (i + head_skip) * child_block_size, | ||
1547 | file_start_offset, | ||
1548 | desired_length); | ||
1549 | } | ||
1550 | return dr; | ||
1551 | } | ||
1552 | |||
1553 | |||
1554 | /** | ||
1555 | * Continuation after a possible attempt to reconstruct | ||
1556 | * the current IBlock from the existing file. | ||
1557 | * | ||
1558 | * @param cls the 'struct ReconstructContext' | ||
1559 | */ | ||
1560 | static void | ||
1561 | reconstruct_cont (void *cls) | ||
1562 | { | ||
1563 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1564 | |||
1565 | /* clean up state from tree encoder */ | ||
1566 | if (NULL != dc->task) | ||
1567 | { | ||
1568 | GNUNET_SCHEDULER_cancel (dc->task); | ||
1569 | dc->task = NULL; | ||
1570 | } | ||
1571 | if (NULL != dc->rfh) | ||
1572 | { | ||
1573 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh)); | ||
1574 | dc->rfh = NULL; | ||
1575 | } | ||
1576 | /* start "normal" download */ | ||
1577 | dc->issue_requests = GNUNET_YES; | ||
1578 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting normal download\n"); | ||
1579 | schedule_block_download (dc, dc->top_request); | ||
1580 | } | ||
1581 | |||
1582 | |||
1583 | /** | ||
1584 | * Task requesting the next block from the tree encoder. | ||
1585 | * | ||
1586 | * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing | ||
1587 | */ | ||
1588 | static void | ||
1589 | get_next_block (void *cls) | ||
1590 | { | ||
1591 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1592 | |||
1593 | dc->task = NULL; | ||
1594 | GNUNET_FS_tree_encoder_next (dc->te); | ||
1595 | } | ||
1596 | |||
1597 | |||
1598 | /** | ||
1599 | * Function called asking for the current (encoded) | ||
1600 | * block to be processed. After processing the | ||
1601 | * client should either call "GNUNET_FS_tree_encode_next" | ||
1602 | * or (on error) "GNUNET_FS_tree_encode_finish". | ||
1603 | * | ||
1604 | * This function checks if the content on disk matches | ||
1605 | * the expected content based on the URI. | ||
1606 | * | ||
1607 | * @param cls closure | ||
1608 | * @param chk content hash key for the block | ||
1609 | * @param offset offset of the block | ||
1610 | * @param depth depth of the block, 0 for DBLOCK | ||
1611 | * @param type type of the block (IBLOCK or DBLOCK) | ||
1612 | * @param block the (encrypted) block | ||
1613 | * @param block_size size of block (in bytes) | ||
1614 | */ | ||
1615 | static void | ||
1616 | reconstruct_cb (void *cls, | ||
1617 | const struct ContentHashKey *chk, | ||
1618 | uint64_t offset, | ||
1619 | unsigned int depth, | ||
1620 | enum GNUNET_BLOCK_Type type, | ||
1621 | const void *block, | ||
1622 | uint16_t block_size) | ||
1623 | { | ||
1624 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1625 | struct GNUNET_FS_ProgressInfo pi; | ||
1626 | struct DownloadRequest *dr; | ||
1627 | uint64_t blen; | ||
1628 | unsigned int chld; | ||
1629 | |||
1630 | /* find corresponding request entry */ | ||
1631 | dr = dc->top_request; | ||
1632 | while (dr->depth > depth) | ||
1633 | { | ||
1634 | GNUNET_assert (dr->num_children > 0); | ||
1635 | blen = GNUNET_FS_tree_compute_tree_size (dr->depth - 1); | ||
1636 | chld = (offset - dr->offset) / blen; | ||
1637 | if (chld < dr->children[0]->chk_idx) | ||
1638 | { | ||
1639 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1640 | "Block %u < %u irrelevant for our range\n", | ||
1641 | chld, | ||
1642 | dr->children[0]->chk_idx); | ||
1643 | dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc); | ||
1644 | return; /* irrelevant block */ | ||
1645 | } | ||
1646 | if (chld > dr->children[dr->num_children - 1]->chk_idx) | ||
1647 | { | ||
1648 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1649 | "Block %u > %u irrelevant for our range\n", | ||
1650 | chld, | ||
1651 | dr->children[dr->num_children - 1]->chk_idx); | ||
1652 | dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc); | ||
1653 | return; /* irrelevant block */ | ||
1654 | } | ||
1655 | dr = dr->children[chld - dr->children[0]->chk_idx]; | ||
1656 | } | ||
1657 | GNUNET_log ( | ||
1658 | GNUNET_ERROR_TYPE_DEBUG, | ||
1659 | "Matched TE block with request at offset %llu and depth %u in state %d\n", | ||
1660 | (unsigned long long) dr->offset, | ||
1661 | dr->depth, | ||
1662 | dr->state); | ||
1663 | /* FIXME: this code needs more testing and might | ||
1664 | need to handle more states... */ | ||
1665 | switch (dr->state) | ||
1666 | { | ||
1667 | case BRS_INIT: | ||
1668 | break; | ||
1669 | |||
1670 | case BRS_RECONSTRUCT_DOWN: | ||
1671 | break; | ||
1672 | |||
1673 | case BRS_RECONSTRUCT_META_UP: | ||
1674 | break; | ||
1675 | |||
1676 | case BRS_RECONSTRUCT_UP: | ||
1677 | break; | ||
1678 | |||
1679 | case BRS_CHK_SET: | ||
1680 | if (0 == memcmp (chk, &dr->chk, sizeof(struct ContentHashKey))) | ||
1681 | { | ||
1682 | GNUNET_log ( | ||
1683 | GNUNET_ERROR_TYPE_DEBUG, | ||
1684 | "Reconstruction succeeded, can use block at offset %llu, depth %u\n", | ||
1685 | (unsigned long long) offset, | ||
1686 | depth); | ||
1687 | /* block matches, hence tree below matches; | ||
1688 | * this request is done! */ | ||
1689 | dr->state = BRS_DOWNLOAD_UP; | ||
1690 | (void) GNUNET_CONTAINER_multihashmap_remove (dc->active, | ||
1691 | &dr->chk.query, | ||
1692 | dr); | ||
1693 | /* calculate how many bytes of payload this block | ||
1694 | * corresponds to */ | ||
1695 | blen = GNUNET_FS_tree_compute_tree_size (dr->depth); | ||
1696 | /* how many of those bytes are in the requested range? */ | ||
1697 | blen = GNUNET_MIN (blen, dc->length + dc->offset - dr->offset); | ||
1698 | /* signal progress */ | ||
1699 | dc->completed += blen; | ||
1700 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS; | ||
1701 | pi.value.download.specifics.progress.data = NULL; | ||
1702 | pi.value.download.specifics.progress.offset = offset; | ||
1703 | pi.value.download.specifics.progress.data_len = 0; | ||
1704 | pi.value.download.specifics.progress.depth = 0; | ||
1705 | pi.value.download.specifics.progress.respect_offered = 0; | ||
1706 | pi.value.download.specifics.progress.block_download_duration = | ||
1707 | GNUNET_TIME_UNIT_ZERO; | ||
1708 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1709 | /* FIXME: duplicated code from 'process_result_with_request - refactor */ | ||
1710 | if (dc->completed == dc->length) | ||
1711 | { | ||
1712 | /* download completed, signal */ | ||
1713 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1714 | "Download completed, truncating file to desired length %llu\n", | ||
1715 | (unsigned long long) GNUNET_ntohll ( | ||
1716 | dc->uri->data.chk.file_length)); | ||
1717 | /* truncate file to size (since we store IBlocks at the end) */ | ||
1718 | if (NULL != dc->filename) | ||
1719 | { | ||
1720 | if (0 != truncate (dc->filename, | ||
1721 | GNUNET_ntohll (dc->uri->data.chk.file_length))) | ||
1722 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
1723 | "truncate", | ||
1724 | dc->filename); | ||
1725 | } | ||
1726 | } | ||
1727 | } | ||
1728 | else | ||
1729 | GNUNET_log ( | ||
1730 | GNUNET_ERROR_TYPE_DEBUG, | ||
1731 | "Reconstruction failed, need to download block at offset %llu, depth %u\n", | ||
1732 | (unsigned long long) offset, | ||
1733 | depth); | ||
1734 | break; | ||
1735 | |||
1736 | case BRS_DOWNLOAD_DOWN: | ||
1737 | break; | ||
1738 | |||
1739 | case BRS_DOWNLOAD_UP: | ||
1740 | break; | ||
1741 | |||
1742 | case BRS_ERROR: | ||
1743 | break; | ||
1744 | |||
1745 | default: | ||
1746 | GNUNET_assert (0); | ||
1747 | break; | ||
1748 | } | ||
1749 | dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc); | ||
1750 | if ((dr == dc->top_request) && (dr->state == BRS_DOWNLOAD_UP)) | ||
1751 | check_completed (dc); | ||
1752 | } | ||
1753 | |||
1754 | |||
1755 | /** | ||
1756 | * Function called by the tree encoder to obtain a block of plaintext | ||
1757 | * data (for the lowest level of the tree). | ||
1758 | * | ||
1759 | * @param cls our 'struct ReconstructContext' | ||
1760 | * @param offset identifies which block to get | ||
1761 | * @param max (maximum) number of bytes to get; returning | ||
1762 | * fewer will also cause errors | ||
1763 | * @param buf where to copy the plaintext buffer | ||
1764 | * @param emsg location to store an error message (on error) | ||
1765 | * @return number of bytes copied to buf, 0 on error | ||
1766 | */ | ||
1767 | static size_t | ||
1768 | fh_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg) | ||
1769 | { | ||
1770 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1771 | struct GNUNET_DISK_FileHandle *fh = dc->rfh; | ||
1772 | ssize_t ret; | ||
1773 | |||
1774 | if (NULL != emsg) | ||
1775 | *emsg = NULL; | ||
1776 | if (offset != GNUNET_DISK_file_seek (fh, offset, GNUNET_DISK_SEEK_SET)) | ||
1777 | { | ||
1778 | if (NULL != emsg) | ||
1779 | *emsg = GNUNET_strdup (strerror (errno)); | ||
1780 | return 0; | ||
1781 | } | ||
1782 | ret = GNUNET_DISK_file_read (fh, buf, max); | ||
1783 | if (ret < 0) | ||
1784 | { | ||
1785 | if (NULL != emsg) | ||
1786 | *emsg = GNUNET_strdup (strerror (errno)); | ||
1787 | return 0; | ||
1788 | } | ||
1789 | return ret; | ||
1790 | } | ||
1791 | |||
1792 | |||
1793 | /** | ||
1794 | * Task that creates the initial (top-level) download | ||
1795 | * request for the file. | ||
1796 | * | ||
1797 | * @param cls the 'struct GNUNET_FS_DownloadContext' | ||
1798 | */ | ||
1799 | void | ||
1800 | GNUNET_FS_download_start_task_ (void *cls) | ||
1801 | { | ||
1802 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1803 | struct GNUNET_FS_ProgressInfo pi; | ||
1804 | struct GNUNET_DISK_FileHandle *fh; | ||
1805 | |||
1806 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start task running...\n"); | ||
1807 | dc->task = NULL; | ||
1808 | if (0 == dc->length) | ||
1809 | { | ||
1810 | /* no bytes required! */ | ||
1811 | if (NULL != dc->filename) | ||
1812 | { | ||
1813 | fh = GNUNET_DISK_file_open (dc->filename, | ||
1814 | GNUNET_DISK_OPEN_READWRITE | ||
1815 | | GNUNET_DISK_OPEN_CREATE | ||
1816 | | ((0 == | ||
1817 | GNUNET_FS_uri_chk_get_file_size (dc->uri)) | ||
1818 | ? GNUNET_DISK_OPEN_TRUNCATE | ||
1819 | : 0), | ||
1820 | GNUNET_DISK_PERM_USER_READ | ||
1821 | | GNUNET_DISK_PERM_USER_WRITE | ||
1822 | | GNUNET_DISK_PERM_GROUP_READ | ||
1823 | | GNUNET_DISK_PERM_OTHER_READ); | ||
1824 | GNUNET_DISK_file_close (fh); | ||
1825 | } | ||
1826 | GNUNET_FS_download_sync_ (dc); | ||
1827 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_START; | ||
1828 | pi.value.download.specifics.start.meta = dc->meta; | ||
1829 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1830 | check_completed (dc); | ||
1831 | return; | ||
1832 | } | ||
1833 | if (NULL != dc->emsg) | ||
1834 | return; | ||
1835 | if (NULL == dc->top_request) | ||
1836 | { | ||
1837 | dc->top_request = create_download_request (NULL, | ||
1838 | 0, | ||
1839 | dc->treedepth - 1, | ||
1840 | 0, | ||
1841 | dc->offset, | ||
1842 | dc->length); | ||
1843 | dc->top_request->state = BRS_CHK_SET; | ||
1844 | dc->top_request->chk = (dc->uri->type == GNUNET_FS_URI_CHK) | ||
1845 | ? dc->uri->data.chk.chk | ||
1846 | : dc->uri->data.loc.fi.chk; | ||
1847 | /* signal start */ | ||
1848 | GNUNET_FS_download_sync_ (dc); | ||
1849 | if (NULL != dc->search) | ||
1850 | GNUNET_FS_search_result_sync_ (dc->search); | ||
1851 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_START; | ||
1852 | pi.value.download.specifics.start.meta = dc->meta; | ||
1853 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1854 | } | ||
1855 | GNUNET_FS_download_start_downloading_ (dc); | ||
1856 | /* attempt reconstruction from disk */ | ||
1857 | if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename)) | ||
1858 | dc->rfh = GNUNET_DISK_file_open (dc->filename, | ||
1859 | GNUNET_DISK_OPEN_READ, | ||
1860 | GNUNET_DISK_PERM_NONE); | ||
1861 | if (dc->top_request->state == BRS_CHK_SET) | ||
1862 | { | ||
1863 | if (NULL != dc->rfh) | ||
1864 | { | ||
1865 | /* first, try top-down */ | ||
1866 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1867 | "Trying top-down reconstruction for `%s'\n", | ||
1868 | dc->filename); | ||
1869 | try_top_down_reconstruction (dc, dc->top_request); | ||
1870 | switch (dc->top_request->state) | ||
1871 | { | ||
1872 | case BRS_CHK_SET: | ||
1873 | break; /* normal */ | ||
1874 | |||
1875 | case BRS_DOWNLOAD_DOWN: | ||
1876 | break; /* normal, some blocks already down */ | ||
1877 | |||
1878 | case BRS_DOWNLOAD_UP: | ||
1879 | /* already done entirely, party! */ | ||
1880 | if (NULL != dc->rfh) | ||
1881 | { | ||
1882 | /* avoid hanging on to file handle longer than | ||
1883 | * necessary */ | ||
1884 | GNUNET_DISK_file_close (dc->rfh); | ||
1885 | dc->rfh = NULL; | ||
1886 | } | ||
1887 | return; | ||
1888 | |||
1889 | case BRS_ERROR: | ||
1890 | GNUNET_asprintf (&dc->emsg, _ ("Invalid URI")); | ||
1891 | GNUNET_FS_download_sync_ (dc); | ||
1892 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR; | ||
1893 | pi.value.download.specifics.error.message = dc->emsg; | ||
1894 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1895 | return; | ||
1896 | |||
1897 | default: | ||
1898 | GNUNET_assert (0); | ||
1899 | break; | ||
1900 | } | ||
1901 | } | ||
1902 | } | ||
1903 | /* attempt reconstruction from meta data */ | ||
1904 | if ((GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) && | ||
1905 | (NULL != dc->meta)) | ||
1906 | { | ||
1907 | GNUNET_log ( | ||
1908 | GNUNET_ERROR_TYPE_DEBUG, | ||
1909 | "Trying to find embedded meta data for download of size %llu with %u bytes MD\n", | ||
1910 | (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri), | ||
1911 | (unsigned int) GNUNET_FS_meta_data_get_serialized_size (dc->meta)); | ||
1912 | GNUNET_FS_meta_data_iterate (dc->meta, &match_full_data, dc); | ||
1913 | if (BRS_DOWNLOAD_UP == dc->top_request->state) | ||
1914 | { | ||
1915 | if (NULL != dc->rfh) | ||
1916 | { | ||
1917 | /* avoid hanging on to file handle longer than | ||
1918 | * necessary */ | ||
1919 | GNUNET_DISK_file_close (dc->rfh); | ||
1920 | dc->rfh = NULL; | ||
1921 | } | ||
1922 | return; /* finished, status update was already done for us */ | ||
1923 | } | ||
1924 | } | ||
1925 | if (NULL != dc->rfh) | ||
1926 | { | ||
1927 | /* finally, actually run bottom-up */ | ||
1928 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1929 | "Trying bottom-up reconstruction of file `%s'\n", | ||
1930 | dc->filename); | ||
1931 | dc->te = | ||
1932 | GNUNET_FS_tree_encoder_create (dc->h, | ||
1933 | GNUNET_FS_uri_chk_get_file_size (dc->uri), | ||
1934 | dc, | ||
1935 | &fh_reader, | ||
1936 | &reconstruct_cb, | ||
1937 | NULL, | ||
1938 | &reconstruct_cont); | ||
1939 | dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc); | ||
1940 | } | ||
1941 | else | ||
1942 | { | ||
1943 | /* simple, top-level download */ | ||
1944 | dc->issue_requests = GNUNET_YES; | ||
1945 | schedule_block_download (dc, dc->top_request); | ||
1946 | } | ||
1947 | if (BRS_DOWNLOAD_UP == dc->top_request->state) | ||
1948 | check_completed (dc); | ||
1949 | } | ||
1950 | |||
1951 | |||
1952 | void | ||
1953 | GNUNET_FS_download_signal_suspend_ (void *cls) | ||
1954 | { | ||
1955 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1956 | struct GNUNET_FS_ProgressInfo pi; | ||
1957 | |||
1958 | if (NULL != dc->top) | ||
1959 | GNUNET_FS_end_top (dc->h, dc->top); | ||
1960 | while (NULL != dc->child_head) | ||
1961 | GNUNET_FS_download_signal_suspend_ (dc->child_head); | ||
1962 | if (NULL != dc->search) | ||
1963 | { | ||
1964 | dc->search->download = NULL; | ||
1965 | dc->search = NULL; | ||
1966 | } | ||
1967 | if (NULL != dc->job_queue) | ||
1968 | { | ||
1969 | GNUNET_FS_dequeue_ (dc->job_queue); | ||
1970 | dc->job_queue = NULL; | ||
1971 | } | ||
1972 | if (NULL != dc->parent) | ||
1973 | GNUNET_CONTAINER_DLL_remove (dc->parent->child_head, | ||
1974 | dc->parent->child_tail, | ||
1975 | dc); | ||
1976 | if (NULL != dc->task) | ||
1977 | { | ||
1978 | GNUNET_SCHEDULER_cancel (dc->task); | ||
1979 | dc->task = NULL; | ||
1980 | } | ||
1981 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND; | ||
1982 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1983 | if (NULL != dc->te) | ||
1984 | { | ||
1985 | GNUNET_FS_tree_encoder_finish (dc->te, NULL); | ||
1986 | dc->te = NULL; | ||
1987 | } | ||
1988 | if (NULL != dc->rfh) | ||
1989 | { | ||
1990 | GNUNET_DISK_file_close (dc->rfh); | ||
1991 | dc->rfh = NULL; | ||
1992 | } | ||
1993 | GNUNET_FS_free_download_request_ (dc->top_request); | ||
1994 | if (NULL != dc->active) | ||
1995 | { | ||
1996 | GNUNET_CONTAINER_multihashmap_destroy (dc->active); | ||
1997 | dc->active = NULL; | ||
1998 | } | ||
1999 | GNUNET_free (dc->filename); | ||
2000 | GNUNET_FS_meta_data_destroy (dc->meta); | ||
2001 | GNUNET_FS_uri_destroy (dc->uri); | ||
2002 | GNUNET_free (dc->temp_filename); | ||
2003 | GNUNET_free (dc->serialization); | ||
2004 | GNUNET_assert (NULL == dc->job_queue); | ||
2005 | GNUNET_free (dc); | ||
2006 | } | ||
2007 | |||
2008 | |||
2009 | /** | ||
2010 | * Helper function to setup the download context. | ||
2011 | * | ||
2012 | * @param h handle to the file sharing subsystem | ||
2013 | * @param uri the URI of the file (determines what to download); CHK or LOC URI | ||
2014 | * @param meta known metadata for the file (can be NULL) | ||
2015 | * @param filename where to store the file, maybe NULL (then no file is | ||
2016 | * created on disk and data must be grabbed from the callbacks) | ||
2017 | * @param tempname where to store temporary file data, not used if filename is non-NULL; | ||
2018 | * can be NULL (in which case we will pick a name if needed); the temporary file | ||
2019 | * may already exist, in which case we will try to use the data that is there and | ||
2020 | * if it is not what is desired, will overwrite it | ||
2021 | * @param offset at what offset should we start the download (typically 0) | ||
2022 | * @param length how many bytes should be downloaded starting at offset | ||
2023 | * @param anonymity anonymity level to use for the download | ||
2024 | * @param options various options | ||
2025 | * @param cctx initial value for the client context for this download | ||
2026 | * @return context that can be used to control this download | ||
2027 | */ | ||
2028 | struct GNUNET_FS_DownloadContext * | ||
2029 | create_download_context (struct GNUNET_FS_Handle *h, | ||
2030 | const struct GNUNET_FS_Uri *uri, | ||
2031 | const struct GNUNET_FS_MetaData *meta, | ||
2032 | const char *filename, | ||
2033 | const char *tempname, | ||
2034 | uint64_t offset, | ||
2035 | uint64_t length, | ||
2036 | uint32_t anonymity, | ||
2037 | enum GNUNET_FS_DownloadOptions options, | ||
2038 | void *cctx) | ||
2039 | { | ||
2040 | struct GNUNET_FS_DownloadContext *dc; | ||
2041 | |||
2042 | GNUNET_assert (GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri)); | ||
2043 | if ((offset + length < offset) || | ||
2044 | (offset + length > GNUNET_FS_uri_chk_get_file_size (uri))) | ||
2045 | { | ||
2046 | GNUNET_break (0); | ||
2047 | return NULL; | ||
2048 | } | ||
2049 | dc = GNUNET_new (struct GNUNET_FS_DownloadContext); | ||
2050 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2051 | "Starting download %p, %u bytes at offset %llu\n", | ||
2052 | dc, | ||
2053 | (unsigned int) length, | ||
2054 | (unsigned long long) offset); | ||
2055 | dc->h = h; | ||
2056 | dc->uri = GNUNET_FS_uri_dup (uri); | ||
2057 | dc->meta = GNUNET_FS_meta_data_duplicate (meta); | ||
2058 | dc->client_info = cctx; | ||
2059 | dc->start_time = GNUNET_TIME_absolute_get (); | ||
2060 | if (NULL != filename) | ||
2061 | { | ||
2062 | dc->filename = GNUNET_strdup (filename); | ||
2063 | if (GNUNET_YES == GNUNET_DISK_file_test (filename)) | ||
2064 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_size (filename, | ||
2065 | &dc->old_file_size, | ||
2066 | GNUNET_YES, | ||
2067 | GNUNET_YES)); | ||
2068 | } | ||
2069 | if (GNUNET_FS_uri_test_loc (dc->uri)) | ||
2070 | GNUNET_assert (GNUNET_OK == | ||
2071 | GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target)); | ||
2072 | dc->offset = offset; | ||
2073 | dc->length = length; | ||
2074 | dc->anonymity = anonymity; | ||
2075 | dc->options = options; | ||
2076 | dc->active = | ||
2077 | GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE), | ||
2078 | GNUNET_NO); | ||
2079 | dc->treedepth = | ||
2080 | GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri)); | ||
2081 | if ((NULL == filename) && (is_recursive_download (dc))) | ||
2082 | { | ||
2083 | if (NULL != tempname) | ||
2084 | dc->temp_filename = GNUNET_strdup (tempname); | ||
2085 | else | ||
2086 | dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp"); | ||
2087 | } | ||
2088 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2089 | "Starting download `%s' of %llu bytes with tree depth %u\n", | ||
2090 | filename, | ||
2091 | (unsigned long long) length, | ||
2092 | dc->treedepth); | ||
2093 | GNUNET_assert (NULL == dc->job_queue); | ||
2094 | dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc); | ||
2095 | return dc; | ||
2096 | } | ||
2097 | |||
2098 | |||
2099 | struct GNUNET_FS_DownloadContext * | ||
2100 | GNUNET_FS_download_start (struct GNUNET_FS_Handle *h, | ||
2101 | const struct GNUNET_FS_Uri *uri, | ||
2102 | const struct GNUNET_FS_MetaData *meta, | ||
2103 | const char *filename, | ||
2104 | const char *tempname, | ||
2105 | uint64_t offset, | ||
2106 | uint64_t length, | ||
2107 | uint32_t anonymity, | ||
2108 | enum GNUNET_FS_DownloadOptions options, | ||
2109 | void *cctx, | ||
2110 | struct GNUNET_FS_DownloadContext *parent) | ||
2111 | { | ||
2112 | struct GNUNET_FS_DownloadContext *dc; | ||
2113 | |||
2114 | dc = create_download_context (h, | ||
2115 | uri, | ||
2116 | meta, | ||
2117 | filename, | ||
2118 | tempname, | ||
2119 | offset, | ||
2120 | length, | ||
2121 | anonymity, | ||
2122 | options, | ||
2123 | cctx); | ||
2124 | if (NULL == dc) | ||
2125 | return NULL; | ||
2126 | dc->parent = parent; | ||
2127 | if (NULL != parent) | ||
2128 | GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc); | ||
2129 | else if (0 == (GNUNET_FS_DOWNLOAD_IS_PROBE & options)) | ||
2130 | dc->top = | ||
2131 | GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc); | ||
2132 | return dc; | ||
2133 | } | ||
2134 | |||
2135 | |||
2136 | struct GNUNET_FS_DownloadContext * | ||
2137 | GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h, | ||
2138 | struct GNUNET_FS_SearchResult *sr, | ||
2139 | const char *filename, | ||
2140 | const char *tempname, | ||
2141 | uint64_t offset, | ||
2142 | uint64_t length, | ||
2143 | uint32_t anonymity, | ||
2144 | enum GNUNET_FS_DownloadOptions options, | ||
2145 | void *cctx) | ||
2146 | { | ||
2147 | struct GNUNET_FS_DownloadContext *dc; | ||
2148 | |||
2149 | if ((NULL == sr) || (NULL != sr->download)) | ||
2150 | { | ||
2151 | GNUNET_break (0); | ||
2152 | return NULL; | ||
2153 | } | ||
2154 | dc = create_download_context (h, | ||
2155 | sr->uri, | ||
2156 | sr->meta, | ||
2157 | filename, | ||
2158 | tempname, | ||
2159 | offset, | ||
2160 | length, | ||
2161 | anonymity, | ||
2162 | options, | ||
2163 | cctx); | ||
2164 | if (NULL == dc) | ||
2165 | return NULL; | ||
2166 | dc->search = sr; | ||
2167 | sr->download = dc; | ||
2168 | if (NULL != sr->probe_ctx) | ||
2169 | { | ||
2170 | GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES); | ||
2171 | sr->probe_ctx = NULL; | ||
2172 | GNUNET_FS_stop_probe_ping_task_ (sr); | ||
2173 | } | ||
2174 | return dc; | ||
2175 | } | ||
2176 | |||
2177 | |||
2178 | /** | ||
2179 | * Start the downloading process (by entering the queue). | ||
2180 | * | ||
2181 | * @param dc our download context | ||
2182 | */ | ||
2183 | void | ||
2184 | GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc) | ||
2185 | { | ||
2186 | if (dc->completed == dc->length) | ||
2187 | return; | ||
2188 | if (NULL != dc->mq) | ||
2189 | return; /* already running */ | ||
2190 | GNUNET_assert (NULL == dc->job_queue); | ||
2191 | GNUNET_assert (NULL == dc->task); | ||
2192 | GNUNET_assert (NULL != dc->active); | ||
2193 | dc->job_queue = | ||
2194 | GNUNET_FS_queue_ (dc->h, | ||
2195 | &activate_fs_download, | ||
2196 | &deactivate_fs_download, | ||
2197 | dc, | ||
2198 | (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE, | ||
2199 | (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE)) | ||
2200 | ? GNUNET_FS_QUEUE_PRIORITY_NORMAL | ||
2201 | : GNUNET_FS_QUEUE_PRIORITY_PROBE); | ||
2202 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2203 | "Download %p put into queue as job %p\n", | ||
2204 | dc, | ||
2205 | dc->job_queue); | ||
2206 | } | ||
2207 | |||
2208 | |||
2209 | /** | ||
2210 | * Suspend a download. | ||
2211 | * | ||
2212 | * @param dc handle for the download | ||
2213 | */ | ||
2214 | void | ||
2215 | GNUNET_FS_download_suspend (struct GNUNET_FS_DownloadContext *dc) | ||
2216 | { | ||
2217 | deactivate_fs_download (dc); | ||
2218 | } | ||
2219 | |||
2220 | |||
2221 | /** | ||
2222 | * Resume a suspended download. | ||
2223 | * | ||
2224 | * @param dc handle for the download | ||
2225 | */ | ||
2226 | void | ||
2227 | GNUNET_FS_download_resume (struct GNUNET_FS_DownloadContext *dc) | ||
2228 | { | ||
2229 | struct GNUNET_FS_ProgressInfo pi; | ||
2230 | |||
2231 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE; | ||
2232 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
2233 | |||
2234 | GNUNET_assert (NULL == dc->task); | ||
2235 | dc->job_queue = | ||
2236 | GNUNET_FS_queue_ (dc->h, | ||
2237 | &activate_fs_download, | ||
2238 | &deactivate_fs_download, | ||
2239 | dc, | ||
2240 | (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE, | ||
2241 | (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE)) | ||
2242 | ? GNUNET_FS_QUEUE_PRIORITY_NORMAL | ||
2243 | : GNUNET_FS_QUEUE_PRIORITY_PROBE); | ||
2244 | } | ||
2245 | |||
2246 | |||
2247 | /** | ||
2248 | * Stop a download (aborts if download is incomplete). | ||
2249 | * | ||
2250 | * @param dc handle for the download | ||
2251 | * @param do_delete delete files of incomplete downloads | ||
2252 | */ | ||
2253 | void | ||
2254 | GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc, int do_delete) | ||
2255 | { | ||
2256 | struct GNUNET_FS_ProgressInfo pi; | ||
2257 | int have_children; | ||
2258 | int search_was_null; | ||
2259 | |||
2260 | if (NULL != dc->top) | ||
2261 | GNUNET_FS_end_top (dc->h, dc->top); | ||
2262 | if (NULL != dc->task) | ||
2263 | { | ||
2264 | GNUNET_SCHEDULER_cancel (dc->task); | ||
2265 | dc->task = NULL; | ||
2266 | } | ||
2267 | search_was_null = (NULL == dc->search); | ||
2268 | if (NULL != dc->search) | ||
2269 | { | ||
2270 | dc->search->download = NULL; | ||
2271 | GNUNET_FS_search_result_sync_ (dc->search); | ||
2272 | dc->search = NULL; | ||
2273 | } | ||
2274 | if (NULL != dc->job_queue) | ||
2275 | { | ||
2276 | GNUNET_FS_dequeue_ (dc->job_queue); | ||
2277 | dc->job_queue = NULL; | ||
2278 | } | ||
2279 | if (NULL != dc->te) | ||
2280 | { | ||
2281 | GNUNET_FS_tree_encoder_finish (dc->te, NULL); | ||
2282 | dc->te = NULL; | ||
2283 | } | ||
2284 | have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO; | ||
2285 | while (NULL != dc->child_head) | ||
2286 | GNUNET_FS_download_stop (dc->child_head, do_delete); | ||
2287 | if (NULL != dc->parent) | ||
2288 | GNUNET_CONTAINER_DLL_remove (dc->parent->child_head, | ||
2289 | dc->parent->child_tail, | ||
2290 | dc); | ||
2291 | if (NULL != dc->serialization) | ||
2292 | GNUNET_FS_remove_sync_file_ (dc->h, | ||
2293 | ((NULL != dc->parent) || (! search_was_null)) | ||
2294 | ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD | ||
2295 | : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD, | ||
2296 | dc->serialization); | ||
2297 | if ((GNUNET_YES == have_children) && (NULL == dc->parent)) | ||
2298 | GNUNET_FS_remove_sync_dir_ (dc->h, | ||
2299 | (! search_was_null) | ||
2300 | ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD | ||
2301 | : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD, | ||
2302 | dc->serialization); | ||
2303 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED; | ||
2304 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
2305 | GNUNET_FS_free_download_request_ (dc->top_request); | ||
2306 | dc->top_request = NULL; | ||
2307 | if (NULL != dc->active) | ||
2308 | { | ||
2309 | GNUNET_CONTAINER_multihashmap_destroy (dc->active); | ||
2310 | dc->active = NULL; | ||
2311 | } | ||
2312 | if (NULL != dc->filename) | ||
2313 | { | ||
2314 | if ((dc->completed != dc->length) && (GNUNET_YES == do_delete)) | ||
2315 | { | ||
2316 | if ((0 != unlink (dc->filename)) && (ENOENT != errno)) | ||
2317 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
2318 | "unlink", | ||
2319 | dc->filename); | ||
2320 | } | ||
2321 | GNUNET_free (dc->filename); | ||
2322 | } | ||
2323 | GNUNET_FS_meta_data_destroy (dc->meta); | ||
2324 | GNUNET_FS_uri_destroy (dc->uri); | ||
2325 | if (NULL != dc->temp_filename) | ||
2326 | { | ||
2327 | if (0 != unlink (dc->temp_filename)) | ||
2328 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
2329 | "unlink", | ||
2330 | dc->temp_filename); | ||
2331 | GNUNET_free (dc->temp_filename); | ||
2332 | } | ||
2333 | GNUNET_free (dc->serialization); | ||
2334 | GNUNET_assert (NULL == dc->job_queue); | ||
2335 | GNUNET_free (dc); | ||
2336 | } | ||
2337 | |||
2338 | |||
2339 | /* 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 f23b9da2a..000000000 --- a/src/fs/fs_file_information.c +++ /dev/null | |||
@@ -1,407 +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 | */ | ||
44 | const char * | ||
45 | GNUNET_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 | */ | ||
59 | const char * | ||
60 | GNUNET_FS_file_information_get_filename (const struct | ||
61 | GNUNET_FS_FileInformation *s) | ||
62 | { | ||
63 | return s->filename; | ||
64 | } | ||
65 | |||
66 | |||
67 | /** | ||
68 | * Set the filename in the file information structure. | ||
69 | * If filename was already set, frees it before setting the new one. | ||
70 | * Makes a copy of the argument. | ||
71 | * | ||
72 | * @param s structure to get the filename for | ||
73 | * @param filename filename to set | ||
74 | */ | ||
75 | void | ||
76 | GNUNET_FS_file_information_set_filename (struct GNUNET_FS_FileInformation *s, | ||
77 | const char *filename) | ||
78 | { | ||
79 | GNUNET_free (s->filename); | ||
80 | if (filename) | ||
81 | s->filename = GNUNET_strdup (filename); | ||
82 | else | ||
83 | s->filename = NULL; | ||
84 | } | ||
85 | |||
86 | |||
87 | struct GNUNET_FS_FileInformation * | ||
88 | GNUNET_FS_file_information_create_from_file ( | ||
89 | struct GNUNET_FS_Handle *h, | ||
90 | void *client_info, | ||
91 | const char *filename, | ||
92 | const struct GNUNET_FS_Uri *keywords, | ||
93 | const struct GNUNET_FS_MetaData *meta, | ||
94 | int do_index, | ||
95 | const struct GNUNET_FS_BlockOptions *bo) | ||
96 | { | ||
97 | struct FileInfo *fi; | ||
98 | uint64_t fsize; | ||
99 | struct GNUNET_FS_FileInformation *ret; | ||
100 | const char *fn; | ||
101 | const char *ss; | ||
102 | |||
103 | /* FIXME: should include_symbolic_links be GNUNET_NO or GNUNET_YES here? */ | ||
104 | if (GNUNET_OK != | ||
105 | GNUNET_DISK_file_size (filename, &fsize, GNUNET_NO, GNUNET_YES)) | ||
106 | { | ||
107 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename); | ||
108 | return NULL; | ||
109 | } | ||
110 | fi = GNUNET_FS_make_file_reader_context_ (filename); | ||
111 | if (NULL == fi) | ||
112 | { | ||
113 | GNUNET_break (0); | ||
114 | return NULL; | ||
115 | } | ||
116 | ret = | ||
117 | GNUNET_FS_file_information_create_from_reader (h, | ||
118 | client_info, | ||
119 | fsize, | ||
120 | &GNUNET_FS_data_reader_file_, | ||
121 | fi, | ||
122 | keywords, | ||
123 | meta, | ||
124 | do_index, | ||
125 | bo); | ||
126 | if (ret == NULL) | ||
127 | return NULL; | ||
128 | ret->h = h; | ||
129 | ret->filename = GNUNET_strdup (filename); | ||
130 | fn = filename; | ||
131 | while (NULL != (ss = strstr (fn, DIR_SEPARATOR_STR))) | ||
132 | fn = ss + 1; | ||
133 | /* FIXME: If we assume that on other platforms CRT is UTF-8-aware, then | ||
134 | * this should be changed to EXTRACTOR_METAFORMAT_UTF8 | ||
135 | */ | ||
136 | GNUNET_FS_meta_data_insert (ret->meta, | ||
137 | "<gnunet>", | ||
138 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME, | ||
139 | EXTRACTOR_METAFORMAT_C_STRING, | ||
140 | "text/plain", | ||
141 | fn, | ||
142 | strlen (fn) + 1); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | |||
147 | struct GNUNET_FS_FileInformation * | ||
148 | GNUNET_FS_file_information_create_from_data ( | ||
149 | struct GNUNET_FS_Handle *h, | ||
150 | void *client_info, | ||
151 | uint64_t length, | ||
152 | void *data, | ||
153 | const struct GNUNET_FS_Uri *keywords, | ||
154 | const struct GNUNET_FS_MetaData *meta, | ||
155 | int do_index, | ||
156 | const struct GNUNET_FS_BlockOptions *bo) | ||
157 | { | ||
158 | if (GNUNET_YES == do_index) | ||
159 | { | ||
160 | GNUNET_break (0); | ||
161 | return NULL; | ||
162 | } | ||
163 | return GNUNET_FS_file_information_create_from_reader (h, | ||
164 | client_info, | ||
165 | length, | ||
166 | & | ||
167 | GNUNET_FS_data_reader_copy_, | ||
168 | data, | ||
169 | keywords, | ||
170 | meta, | ||
171 | do_index, | ||
172 | bo); | ||
173 | } | ||
174 | |||
175 | |||
176 | struct GNUNET_FS_FileInformation * | ||
177 | GNUNET_FS_file_information_create_from_reader ( | ||
178 | struct GNUNET_FS_Handle *h, | ||
179 | void *client_info, | ||
180 | uint64_t length, | ||
181 | GNUNET_FS_DataReader reader, | ||
182 | void *reader_cls, | ||
183 | const struct GNUNET_FS_Uri *keywords, | ||
184 | const struct GNUNET_FS_MetaData *meta, | ||
185 | int do_index, | ||
186 | const struct GNUNET_FS_BlockOptions *bo) | ||
187 | { | ||
188 | struct GNUNET_FS_FileInformation *ret; | ||
189 | |||
190 | if ((GNUNET_YES == do_index) && (reader != &GNUNET_FS_data_reader_file_)) | ||
191 | { | ||
192 | GNUNET_break (0); | ||
193 | return NULL; | ||
194 | } | ||
195 | ret = GNUNET_new (struct GNUNET_FS_FileInformation); | ||
196 | ret->h = h; | ||
197 | ret->client_info = client_info; | ||
198 | ret->meta = GNUNET_FS_meta_data_duplicate (meta); | ||
199 | if (ret->meta == NULL) | ||
200 | ret->meta = GNUNET_FS_meta_data_create (); | ||
201 | ret->keywords = (keywords == NULL) ? NULL : GNUNET_FS_uri_dup (keywords); | ||
202 | ret->data.file.reader = reader; | ||
203 | ret->data.file.reader_cls = reader_cls; | ||
204 | ret->data.file.do_index = do_index; | ||
205 | ret->data.file.file_size = length; | ||
206 | ret->bo = *bo; | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | |||
211 | /** | ||
212 | * Test if a given entry represents a directory. | ||
213 | * | ||
214 | * @param ent check if this FI represents a directory | ||
215 | * @return #GNUNET_YES if so, #GNUNET_NO if not | ||
216 | */ | ||
217 | int | ||
218 | GNUNET_FS_file_information_is_directory ( | ||
219 | const struct GNUNET_FS_FileInformation *ent) | ||
220 | { | ||
221 | return ent->is_directory; | ||
222 | } | ||
223 | |||
224 | |||
225 | struct GNUNET_FS_FileInformation * | ||
226 | GNUNET_FS_file_information_create_empty_directory ( | ||
227 | struct GNUNET_FS_Handle *h, | ||
228 | void *client_info, | ||
229 | const struct GNUNET_FS_Uri *keywords, | ||
230 | const struct GNUNET_FS_MetaData *meta, | ||
231 | const struct GNUNET_FS_BlockOptions *bo, | ||
232 | const char *filename) | ||
233 | { | ||
234 | struct GNUNET_FS_FileInformation *ret; | ||
235 | |||
236 | ret = GNUNET_new (struct GNUNET_FS_FileInformation); | ||
237 | ret->h = h; | ||
238 | ret->client_info = client_info; | ||
239 | ret->meta = GNUNET_FS_meta_data_duplicate (meta); | ||
240 | ret->keywords = GNUNET_FS_uri_dup (keywords); | ||
241 | ret->bo = *bo; | ||
242 | ret->is_directory = GNUNET_YES; | ||
243 | if (filename != NULL) | ||
244 | ret->filename = GNUNET_strdup (filename); | ||
245 | return ret; | ||
246 | } | ||
247 | |||
248 | |||
249 | int | ||
250 | GNUNET_FS_file_information_add (struct GNUNET_FS_FileInformation *dir, | ||
251 | struct GNUNET_FS_FileInformation *ent) | ||
252 | { | ||
253 | if ((ent->dir != NULL) || (ent->next != NULL) || | ||
254 | (dir->is_directory != GNUNET_YES)) | ||
255 | { | ||
256 | GNUNET_break (0); | ||
257 | return GNUNET_SYSERR; | ||
258 | } | ||
259 | ent->dir = dir; | ||
260 | ent->next = dir->data.dir.entries; | ||
261 | dir->data.dir.entries = ent; | ||
262 | dir->data.dir.dir_size = 0; | ||
263 | return GNUNET_OK; | ||
264 | } | ||
265 | |||
266 | |||
267 | /** | ||
268 | * Inspect a file or directory in a publish-structure. Clients | ||
269 | * should never modify publish structures that were passed to | ||
270 | * #GNUNET_FS_publish_start already. When called on a directory, | ||
271 | * this function will FIRST call @a proc with information about | ||
272 | * the directory itself and then for each of the files in the | ||
273 | * directory (but not for files in subdirectories). When called | ||
274 | * on a file, @a proc will be called exactly once (with information | ||
275 | * about the specific file). | ||
276 | * | ||
277 | * @param dir the directory | ||
278 | * @param proc function to call on each entry | ||
279 | * @param proc_cls closure for @a proc | ||
280 | */ | ||
281 | void | ||
282 | GNUNET_FS_file_information_inspect (struct GNUNET_FS_FileInformation *dir, | ||
283 | GNUNET_FS_FileInformationProcessor proc, | ||
284 | void *proc_cls) | ||
285 | { | ||
286 | struct GNUNET_FS_FileInformation *pos; | ||
287 | int no; | ||
288 | |||
289 | no = GNUNET_NO; | ||
290 | if (GNUNET_OK != | ||
291 | proc (proc_cls, | ||
292 | dir, | ||
293 | (dir->is_directory == GNUNET_YES) ? dir->data.dir.dir_size | ||
294 | : dir->data.file.file_size, | ||
295 | dir->meta, | ||
296 | &dir->keywords, | ||
297 | &dir->bo, | ||
298 | (dir->is_directory == GNUNET_YES) ? &no : &dir->data.file.do_index, | ||
299 | &dir->client_info)) | ||
300 | return; | ||
301 | if (dir->is_directory != GNUNET_YES) | ||
302 | return; | ||
303 | pos = dir->data.dir.entries; | ||
304 | while (pos != NULL) | ||
305 | { | ||
306 | no = GNUNET_NO; | ||
307 | if (GNUNET_OK != | ||
308 | proc (proc_cls, | ||
309 | pos, | ||
310 | (pos->is_directory == GNUNET_YES) ? pos->data.dir.dir_size | ||
311 | : pos->data.file.file_size, | ||
312 | pos->meta, | ||
313 | &pos->keywords, | ||
314 | &pos->bo, | ||
315 | (pos->is_directory == GNUNET_YES) ? &no | ||
316 | : &pos->data.file.do_index, | ||
317 | &pos->client_info)) | ||
318 | break; | ||
319 | pos = pos->next; | ||
320 | } | ||
321 | } | ||
322 | |||
323 | |||
324 | /** | ||
325 | * Destroy publish-structure. Clients should never destroy publish | ||
326 | * structures that were passed to #GNUNET_FS_publish_start already. | ||
327 | * | ||
328 | * @param fi structure to destroy | ||
329 | * @param cleaner function to call on each entry in the structure | ||
330 | * (useful to clean up client_info); can be NULL; return | ||
331 | * values are ignored | ||
332 | * @param cleaner_cls closure for @a cleaner | ||
333 | */ | ||
334 | void | ||
335 | GNUNET_FS_file_information_destroy (struct GNUNET_FS_FileInformation *fi, | ||
336 | GNUNET_FS_FileInformationProcessor cleaner, | ||
337 | void *cleaner_cls) | ||
338 | { | ||
339 | struct GNUNET_FS_FileInformation *pos; | ||
340 | int no; | ||
341 | |||
342 | no = GNUNET_NO; | ||
343 | if (GNUNET_YES == fi->is_directory) | ||
344 | { | ||
345 | /* clean up directory */ | ||
346 | while (NULL != (pos = fi->data.dir.entries)) | ||
347 | { | ||
348 | fi->data.dir.entries = pos->next; | ||
349 | GNUNET_FS_file_information_destroy (pos, cleaner, cleaner_cls); | ||
350 | } | ||
351 | /* clean up client-info */ | ||
352 | if (NULL != cleaner) | ||
353 | cleaner (cleaner_cls, | ||
354 | fi, | ||
355 | fi->data.dir.dir_size, | ||
356 | fi->meta, | ||
357 | &fi->keywords, | ||
358 | &fi->bo, | ||
359 | &no, | ||
360 | &fi->client_info); | ||
361 | GNUNET_free (fi->data.dir.dir_data); | ||
362 | } | ||
363 | else | ||
364 | { | ||
365 | /* call clean-up function of the reader */ | ||
366 | if (NULL != fi->data.file.reader) | ||
367 | { | ||
368 | (void) fi->data.file.reader (fi->data.file.reader_cls, 0, 0, NULL, NULL); | ||
369 | fi->data.file.reader = NULL; | ||
370 | } | ||
371 | /* clean up client-info */ | ||
372 | if (NULL != cleaner) | ||
373 | cleaner (cleaner_cls, | ||
374 | fi, | ||
375 | fi->data.file.file_size, | ||
376 | fi->meta, | ||
377 | &fi->keywords, | ||
378 | &fi->bo, | ||
379 | &fi->data.file.do_index, | ||
380 | &fi->client_info); | ||
381 | } | ||
382 | GNUNET_free (fi->filename); | ||
383 | GNUNET_free (fi->emsg); | ||
384 | if (NULL != fi->sks_uri) | ||
385 | GNUNET_FS_uri_destroy (fi->sks_uri); | ||
386 | if (NULL != fi->chk_uri) | ||
387 | GNUNET_FS_uri_destroy (fi->chk_uri); | ||
388 | /* clean up serialization */ | ||
389 | if ((NULL != fi->serialization) && (0 != unlink (fi->serialization))) | ||
390 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
391 | "unlink", | ||
392 | fi->serialization); | ||
393 | if (NULL != fi->keywords) | ||
394 | GNUNET_FS_uri_destroy (fi->keywords); | ||
395 | if (NULL != fi->meta) | ||
396 | GNUNET_FS_meta_data_destroy (fi->meta); | ||
397 | GNUNET_free (fi->serialization); | ||
398 | if (NULL != fi->te) | ||
399 | { | ||
400 | GNUNET_FS_tree_encoder_finish (fi->te, NULL); | ||
401 | fi->te = NULL; | ||
402 | } | ||
403 | GNUNET_free (fi); | ||
404 | } | ||
405 | |||
406 | |||
407 | /* 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 0135e2e05..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 | |||
28 | #include "gnunet_fs_service.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 | */ | ||
45 | static int | ||
46 | getopt_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 | */ | ||
128 | struct GNUNET_GETOPT_CommandLineOption | ||
129 | GNUNET_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 | */ | ||
161 | static int | ||
162 | getopt_set_metadata (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, | ||
163 | void *scls, | ||
164 | const char *option, | ||
165 | const char *value) | ||
166 | { | ||
167 | struct GNUNET_FS_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_FS_MetaData *meta; | ||
175 | char *tmp; | ||
176 | |||
177 | meta = *mm; | ||
178 | if (meta == NULL) | ||
179 | { | ||
180 | meta = GNUNET_FS_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_FS_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_FS_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_FS_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 | */ | ||
253 | struct GNUNET_GETOPT_CommandLineOption | ||
254 | GNUNET_FS_GETOPT_METADATA (char shortName, | ||
255 | const char *name, | ||
256 | const char *argumentHelp, | ||
257 | const char *description, | ||
258 | struct GNUNET_FS_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 78816cad1..000000000 --- a/src/fs/fs_list_indexed.c +++ /dev/null | |||
@@ -1,219 +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 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | #include "gnunet_protocols.h" | ||
32 | #include "fs_api.h" | ||
33 | |||
34 | |||
35 | /** | ||
36 | * Context for #GNUNET_FS_get_indexed_files(). | ||
37 | */ | ||
38 | struct GNUNET_FS_GetIndexedContext | ||
39 | { | ||
40 | /** | ||
41 | * Connection to the FS service. | ||
42 | */ | ||
43 | struct GNUNET_MQ_Handle *mq; | ||
44 | |||
45 | /** | ||
46 | * Function to call for each indexed file. | ||
47 | */ | ||
48 | GNUNET_FS_IndexedFileProcessor iterator; | ||
49 | |||
50 | /** | ||
51 | * Closure for @e iterator. | ||
52 | */ | ||
53 | void *iterator_cls; | ||
54 | |||
55 | /** | ||
56 | * Continuation to trigger at the end. | ||
57 | */ | ||
58 | GNUNET_SCHEDULER_TaskCallback cont; | ||
59 | |||
60 | /** | ||
61 | * Closure for @e cont. | ||
62 | */ | ||
63 | void *cont_cls; | ||
64 | }; | ||
65 | |||
66 | |||
67 | /** | ||
68 | * Function called on each response from the FS | ||
69 | * service with information about indexed files. | ||
70 | * | ||
71 | * @param cls closure (of type `struct GNUNET_FS_GetIndexedContext *`) | ||
72 | * @param msg message with indexing information | ||
73 | */ | ||
74 | static void | ||
75 | handle_index_info_end (void *cls, | ||
76 | const struct GNUNET_MessageHeader *msg) | ||
77 | { | ||
78 | struct GNUNET_FS_GetIndexedContext *gic = cls; | ||
79 | |||
80 | (void) gic->iterator (gic->iterator_cls, | ||
81 | NULL, | ||
82 | NULL); | ||
83 | GNUNET_FS_get_indexed_files_cancel (gic); | ||
84 | } | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Check validity of response from the FS | ||
89 | * service with information about indexed files. | ||
90 | * | ||
91 | * @param cls closure (of type `struct GNUNET_FS_GetIndexedContext *`) | ||
92 | * @param iim message with indexing information | ||
93 | */ | ||
94 | static int | ||
95 | check_index_info (void *cls, | ||
96 | const struct IndexInfoMessage *iim) | ||
97 | { | ||
98 | uint16_t msize = ntohs (iim->header.size) - sizeof(*iim); | ||
99 | const char *filename; | ||
100 | |||
101 | filename = (const char *) &iim[1]; | ||
102 | if (filename[msize - 1] != '\0') | ||
103 | { | ||
104 | GNUNET_break (0); | ||
105 | return GNUNET_SYSERR; | ||
106 | } | ||
107 | return GNUNET_OK; | ||
108 | } | ||
109 | |||
110 | |||
111 | /** | ||
112 | * Function called on each response from the FS | ||
113 | * service with information about indexed files. | ||
114 | * | ||
115 | * @param cls closure (of type `struct GNUNET_FS_GetIndexedContext *`) | ||
116 | * @param iim message with indexing information | ||
117 | */ | ||
118 | static void | ||
119 | handle_index_info (void *cls, | ||
120 | const struct IndexInfoMessage *iim) | ||
121 | { | ||
122 | struct GNUNET_FS_GetIndexedContext *gic = cls; | ||
123 | const char *filename; | ||
124 | |||
125 | filename = (const char *) &iim[1]; | ||
126 | if (GNUNET_OK != | ||
127 | gic->iterator (gic->iterator_cls, | ||
128 | filename, | ||
129 | &iim->file_id)) | ||
130 | { | ||
131 | GNUNET_FS_get_indexed_files_cancel (gic); | ||
132 | return; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | |||
137 | /** | ||
138 | * Generic error handler, called with the appropriate error code and | ||
139 | * the same closure specified at the creation of the message queue. | ||
140 | * Not every message queue implementation supports an error handler. | ||
141 | * | ||
142 | * @param cls closure with the `struct GNUNET_FS_GetIndexedContent *` | ||
143 | * @param error error code | ||
144 | */ | ||
145 | static void | ||
146 | mq_error_handler (void *cls, | ||
147 | enum GNUNET_MQ_Error error) | ||
148 | { | ||
149 | struct GNUNET_FS_GetIndexedContext *gic = cls; | ||
150 | |||
151 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
152 | "Failed to receive response from `%s' service (error code is %d).\n", | ||
153 | "fs", | ||
154 | error); | ||
155 | (void) gic->iterator (gic->iterator_cls, | ||
156 | NULL, | ||
157 | NULL); | ||
158 | GNUNET_FS_get_indexed_files_cancel (gic); | ||
159 | } | ||
160 | |||
161 | |||
162 | struct GNUNET_FS_GetIndexedContext * | ||
163 | GNUNET_FS_get_indexed_files (struct GNUNET_FS_Handle *h, | ||
164 | GNUNET_FS_IndexedFileProcessor iterator, | ||
165 | void *iterator_cls) | ||
166 | { | ||
167 | struct GNUNET_FS_GetIndexedContext *gic | ||
168 | = GNUNET_new (struct GNUNET_FS_GetIndexedContext); | ||
169 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
170 | GNUNET_MQ_hd_fixed_size (index_info_end, | ||
171 | GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END, | ||
172 | struct GNUNET_MessageHeader, | ||
173 | gic), | ||
174 | GNUNET_MQ_hd_var_size (index_info, | ||
175 | GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY, | ||
176 | struct IndexInfoMessage, | ||
177 | gic), | ||
178 | GNUNET_MQ_handler_end () | ||
179 | }; | ||
180 | struct GNUNET_MQ_Envelope *env; | ||
181 | struct GNUNET_MessageHeader *msg; | ||
182 | |||
183 | gic->mq = GNUNET_CLIENT_connect (h->cfg, | ||
184 | "fs", | ||
185 | handlers, | ||
186 | &mq_error_handler, | ||
187 | h); | ||
188 | if (NULL == gic->mq) | ||
189 | { | ||
190 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
191 | _ ("Failed to not connect to `%s' service.\n"), | ||
192 | "fs"); | ||
193 | GNUNET_free (gic); | ||
194 | return NULL; | ||
195 | } | ||
196 | gic->iterator = iterator; | ||
197 | gic->iterator_cls = iterator_cls; | ||
198 | env = GNUNET_MQ_msg (msg, | ||
199 | GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET); | ||
200 | GNUNET_MQ_send (gic->mq, | ||
201 | env); | ||
202 | return gic; | ||
203 | } | ||
204 | |||
205 | |||
206 | /** | ||
207 | * Cancel iteration over all indexed files. | ||
208 | * | ||
209 | * @param gic operation to cancel | ||
210 | */ | ||
211 | void | ||
212 | GNUNET_FS_get_indexed_files_cancel (struct GNUNET_FS_GetIndexedContext *gic) | ||
213 | { | ||
214 | GNUNET_MQ_destroy (gic->mq); | ||
215 | GNUNET_free (gic); | ||
216 | } | ||
217 | |||
218 | |||
219 | /* 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 a8e23f042..000000000 --- a/src/fs/fs_misc.c +++ /dev/null | |||
@@ -1,165 +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 | |||
28 | #include "gnunet_fs_service.h" | ||
29 | #include "fs_api.h" | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Suggest a filename based on given metadata. | ||
34 | * | ||
35 | * @param md given meta data | ||
36 | * @return NULL if meta data is useless for suggesting a filename | ||
37 | */ | ||
38 | char * | ||
39 | GNUNET_FS_meta_data_suggest_filename (const struct GNUNET_FS_MetaData | ||
40 | *md) | ||
41 | { | ||
42 | static const char *mimeMap[][2] = { | ||
43 | { "application/bz2", ".bz2" }, | ||
44 | { "application/gnunet-directory", ".gnd" }, | ||
45 | { "application/java", ".class" }, | ||
46 | { "application/msword", ".doc" }, | ||
47 | { "application/nar", ".nar" }, | ||
48 | { "application/narinfo", ".narinfo" }, | ||
49 | { "application/ogg", ".ogg" }, | ||
50 | { "application/pdf", ".pdf" }, | ||
51 | { "application/pgp-keys", ".key" }, | ||
52 | { "application/pgp-signature", ".pgp" }, | ||
53 | { "application/postscript", ".ps" }, | ||
54 | { "application/rar", ".rar" }, | ||
55 | { "application/rtf", ".rtf" }, | ||
56 | { "application/xml", ".xml" }, | ||
57 | { "application/x-debian-package", ".deb" }, | ||
58 | { "application/x-dvi", ".dvi" }, | ||
59 | { "application/x-flac", ".flac" }, | ||
60 | { "application/x-gzip", ".gz" }, | ||
61 | { "application/x-java-archive", ".jar" }, | ||
62 | { "application/x-java-vm", ".class" }, | ||
63 | { "application/x-python-code", ".pyc" }, | ||
64 | { "application/x-redhat-package-manager", ".rpm" }, | ||
65 | { "application/x-rpm", ".rpm" }, | ||
66 | { "application/x-tar", ".tar" }, | ||
67 | { "application/x-tex-pk", ".pk" }, | ||
68 | { "application/x-texinfo", ".texinfo" }, | ||
69 | { "application/x-xcf", ".xcf" }, | ||
70 | { "application/x-xfig", ".xfig" }, | ||
71 | { "application/zip", ".zip" }, | ||
72 | |||
73 | { "audio/midi", ".midi" }, | ||
74 | { "audio/mpeg", ".mp3" }, | ||
75 | { "audio/real", ".rm" }, | ||
76 | { "audio/x-wav", ".wav" }, | ||
77 | |||
78 | { "image/gif", ".gif" }, | ||
79 | { "image/jpeg", ".jpg" }, | ||
80 | { "image/pcx", ".pcx" }, | ||
81 | { "image/png", ".png" }, | ||
82 | { "image/tiff", ".tiff" }, | ||
83 | { "image/x-ms-bmp", ".bmp" }, | ||
84 | { "image/x-xpixmap", ".xpm" }, | ||
85 | |||
86 | { "text/css", ".css" }, | ||
87 | { "text/html", ".html" }, | ||
88 | { "text/plain", ".txt" }, | ||
89 | { "text/rtf", ".rtf" }, | ||
90 | { "text/x-c++hdr", ".h++" }, | ||
91 | { "text/x-c++src", ".c++" }, | ||
92 | { "text/x-chdr", ".h" }, | ||
93 | { "text/x-csrc", ".c" }, | ||
94 | { "text/x-java", ".java" }, | ||
95 | { "text/x-moc", ".moc" }, | ||
96 | { "text/x-pascal", ".pas" }, | ||
97 | { "text/x-perl", ".pl" }, | ||
98 | { "text/x-python", ".py" }, | ||
99 | { "text/x-tex", ".tex" }, | ||
100 | |||
101 | { "video/avi", ".avi" }, | ||
102 | { "video/mpeg", ".mpeg" }, | ||
103 | { "video/quicktime", ".qt" }, | ||
104 | { "video/real", ".rm" }, | ||
105 | { "video/x-msvideo", ".avi" }, | ||
106 | { NULL, NULL }, | ||
107 | }; | ||
108 | char *ret; | ||
109 | unsigned int i; | ||
110 | char *mime; | ||
111 | char *base; | ||
112 | const char *ext; | ||
113 | |||
114 | ret = | ||
115 | GNUNET_FS_meta_data_get_by_type (md, | ||
116 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); | ||
117 | if (ret != NULL) | ||
118 | return ret; | ||
119 | ext = NULL; | ||
120 | mime = | ||
121 | GNUNET_FS_meta_data_get_by_type (md, EXTRACTOR_METATYPE_MIMETYPE); | ||
122 | if (mime != NULL) | ||
123 | { | ||
124 | i = 0; | ||
125 | while ((mimeMap[i][0] != NULL) && (0 != strcmp (mime, mimeMap[i][0]))) | ||
126 | i++; | ||
127 | if (mimeMap[i][1] == NULL) | ||
128 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, | ||
129 | _ ("Did not find mime type `%s' in extension list.\n"), mime); | ||
130 | else | ||
131 | ext = mimeMap[i][1]; | ||
132 | GNUNET_free (mime); | ||
133 | } | ||
134 | base = | ||
135 | GNUNET_FS_meta_data_get_first_by_types (md, | ||
136 | EXTRACTOR_METATYPE_TITLE, | ||
137 | EXTRACTOR_METATYPE_BOOK_TITLE, | ||
138 | EXTRACTOR_METATYPE_ORIGINAL_TITLE, | ||
139 | EXTRACTOR_METATYPE_PACKAGE_NAME, | ||
140 | EXTRACTOR_METATYPE_URL, | ||
141 | EXTRACTOR_METATYPE_URI, | ||
142 | EXTRACTOR_METATYPE_DESCRIPTION, | ||
143 | EXTRACTOR_METATYPE_ISRC, | ||
144 | EXTRACTOR_METATYPE_JOURNAL_NAME, | ||
145 | EXTRACTOR_METATYPE_AUTHOR_NAME, | ||
146 | EXTRACTOR_METATYPE_SUBJECT, | ||
147 | EXTRACTOR_METATYPE_ALBUM, | ||
148 | EXTRACTOR_METATYPE_ARTIST, | ||
149 | EXTRACTOR_METATYPE_KEYWORDS, | ||
150 | EXTRACTOR_METATYPE_COMMENT, | ||
151 | EXTRACTOR_METATYPE_UNKNOWN, | ||
152 | -1); | ||
153 | if ((base == NULL) && (ext == NULL)) | ||
154 | return NULL; | ||
155 | if (base == NULL) | ||
156 | return GNUNET_strdup (ext); | ||
157 | if (ext == NULL) | ||
158 | return base; | ||
159 | GNUNET_asprintf (&ret, "%s%s", base, ext); | ||
160 | GNUNET_free (base); | ||
161 | return ret; | ||
162 | } | ||
163 | |||
164 | |||
165 | /* end of fs_misc.c */ | ||
diff --git a/src/fs/fs_namespace.c b/src/fs/fs_namespace.c deleted file mode 100644 index f8b7b91c0..000000000 --- a/src/fs/fs_namespace.c +++ /dev/null | |||
@@ -1,804 +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 | |||
32 | #include "gnunet_fs_service.h" | ||
33 | #include "fs_api.h" | ||
34 | #include "fs_publish_ublock.h" | ||
35 | |||
36 | |||
37 | /** | ||
38 | * Information about an (updateable) node in the | ||
39 | * namespace. | ||
40 | */ | ||
41 | struct NamespaceUpdateNode | ||
42 | { | ||
43 | /** | ||
44 | * Identifier for this node. | ||
45 | */ | ||
46 | char *id; | ||
47 | |||
48 | /** | ||
49 | * Identifier of children of this node. | ||
50 | */ | ||
51 | char *update; | ||
52 | |||
53 | /** | ||
54 | * Metadata for this entry. | ||
55 | */ | ||
56 | struct GNUNET_FS_MetaData *md; | ||
57 | |||
58 | /** | ||
59 | * URI of this entry in the namespace. | ||
60 | */ | ||
61 | struct GNUNET_FS_Uri *uri; | ||
62 | |||
63 | /** | ||
64 | * Namespace update generation ID. Used to ensure | ||
65 | * freshness of the tree_id. | ||
66 | */ | ||
67 | unsigned int nug; | ||
68 | |||
69 | /** | ||
70 | * TREE this entry belongs to (if nug is current). | ||
71 | */ | ||
72 | unsigned int tree_id; | ||
73 | }; | ||
74 | |||
75 | |||
76 | /** | ||
77 | * Handle to update information for a namespace. | ||
78 | */ | ||
79 | struct GNUNET_FS_UpdateInformationGraph | ||
80 | { | ||
81 | /** | ||
82 | * Handle to the FS service context. | ||
83 | */ | ||
84 | struct GNUNET_FS_Handle *h; | ||
85 | |||
86 | /** | ||
87 | * Array with information about nodes in the namespace. | ||
88 | */ | ||
89 | struct NamespaceUpdateNode **update_nodes; | ||
90 | |||
91 | /** | ||
92 | * Private key for the namespace. | ||
93 | */ | ||
94 | struct GNUNET_CRYPTO_EcdsaPrivateKey ns; | ||
95 | |||
96 | /** | ||
97 | * Hash map mapping identifiers of update nodes | ||
98 | * to the update nodes (initialized on-demand). | ||
99 | */ | ||
100 | struct GNUNET_CONTAINER_MultiHashMap *update_map; | ||
101 | |||
102 | /** | ||
103 | * Size of the update nodes array. | ||
104 | */ | ||
105 | unsigned int update_node_count; | ||
106 | |||
107 | /** | ||
108 | * Reference counter. | ||
109 | */ | ||
110 | unsigned int rc; | ||
111 | |||
112 | /** | ||
113 | * Generator for unique nug numbers. | ||
114 | */ | ||
115 | unsigned int nug_gen; | ||
116 | }; | ||
117 | |||
118 | |||
119 | /** | ||
120 | * Return the name of the directory in which we store | ||
121 | * the update information graph for the given local namespace. | ||
122 | * | ||
123 | * @param h file-sharing handle | ||
124 | * @param ns namespace handle | ||
125 | * @return NULL on error, otherwise the name of the directory | ||
126 | */ | ||
127 | static char * | ||
128 | get_update_information_directory ( | ||
129 | struct GNUNET_FS_Handle *h, | ||
130 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns) | ||
131 | { | ||
132 | char *dn; | ||
133 | char *ret; | ||
134 | struct GNUNET_CRYPTO_EcdsaPublicKey pub; | ||
135 | struct GNUNET_HashCode hc; | ||
136 | struct GNUNET_CRYPTO_HashAsciiEncoded enc; | ||
137 | |||
138 | if (GNUNET_OK != | ||
139 | GNUNET_CONFIGURATION_get_value_filename (h->cfg, "FS", "UPDATE_DIR", &dn)) | ||
140 | { | ||
141 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "fs", "UPDATE_DIR"); | ||
142 | return NULL; | ||
143 | } | ||
144 | GNUNET_CRYPTO_ecdsa_key_get_public (ns, &pub); | ||
145 | GNUNET_CRYPTO_hash (&pub, sizeof(pub), &hc); | ||
146 | GNUNET_CRYPTO_hash_to_enc (&hc, &enc); | ||
147 | GNUNET_asprintf (&ret, | ||
148 | "%s%s%s", | ||
149 | dn, | ||
150 | DIR_SEPARATOR_STR, | ||
151 | (const char *) enc.encoding); | ||
152 | GNUNET_free (dn); | ||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | |||
157 | /** | ||
158 | * Release memory occupied by UIG datastructure. | ||
159 | * | ||
160 | * @param uig data structure to free | ||
161 | */ | ||
162 | static void | ||
163 | free_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig) | ||
164 | { | ||
165 | unsigned int i; | ||
166 | struct NamespaceUpdateNode *nsn; | ||
167 | |||
168 | for (i = 0; i < uig->update_node_count; i++) | ||
169 | { | ||
170 | nsn = uig->update_nodes[i]; | ||
171 | GNUNET_FS_meta_data_destroy (nsn->md); | ||
172 | GNUNET_FS_uri_destroy (nsn->uri); | ||
173 | GNUNET_free (nsn->id); | ||
174 | GNUNET_free (nsn->update); | ||
175 | GNUNET_free (nsn); | ||
176 | } | ||
177 | GNUNET_array_grow (uig->update_nodes, uig->update_node_count, 0); | ||
178 | if (NULL != uig->update_map) | ||
179 | GNUNET_CONTAINER_multihashmap_destroy (uig->update_map); | ||
180 | GNUNET_free (uig); | ||
181 | } | ||
182 | |||
183 | |||
184 | /** | ||
185 | * Write a namespace's update node graph to a file. | ||
186 | * | ||
187 | * @param uig update information graph to dump | ||
188 | */ | ||
189 | static void | ||
190 | write_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig) | ||
191 | { | ||
192 | char *fn; | ||
193 | struct GNUNET_BIO_WriteHandle *wh; | ||
194 | unsigned int i; | ||
195 | struct NamespaceUpdateNode *n; | ||
196 | char *uris; | ||
197 | |||
198 | fn = get_update_information_directory (uig->h, &uig->ns); | ||
199 | wh = GNUNET_BIO_write_open_file (fn); | ||
200 | if (NULL == wh) | ||
201 | { | ||
202 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
203 | _ ("Failed to open `%s' for writing: %s\n"), | ||
204 | fn, | ||
205 | strerror (errno)); | ||
206 | GNUNET_free (fn); | ||
207 | return; | ||
208 | } | ||
209 | if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, | ||
210 | "fs-namespace-node-count", | ||
211 | uig->update_node_count)) | ||
212 | goto END; | ||
213 | for (i = 0; i < uig->update_node_count; i++) | ||
214 | { | ||
215 | n = uig->update_nodes[i]; | ||
216 | uris = GNUNET_FS_uri_to_string (n->uri); | ||
217 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
218 | GNUNET_BIO_write_spec_string ("fs-namespace-node-id", n->id), | ||
219 | GNUNET_FS_write_spec_meta_data ("fs-namespace-node-meta", n->md), | ||
220 | GNUNET_BIO_write_spec_string ("fs-namespace-node-update", n->update), | ||
221 | GNUNET_BIO_write_spec_string ("fs-namespace-uris", uris), | ||
222 | GNUNET_BIO_write_spec_end (), | ||
223 | }; | ||
224 | if (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)) | ||
225 | { | ||
226 | GNUNET_free (uris); | ||
227 | break; | ||
228 | } | ||
229 | GNUNET_free (uris); | ||
230 | } | ||
231 | END: | ||
232 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
233 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
234 | _ ("Failed to write `%s': %s\n"), | ||
235 | fn, | ||
236 | strerror (errno)); | ||
237 | GNUNET_free (fn); | ||
238 | } | ||
239 | |||
240 | |||
241 | /** | ||
242 | * Read the namespace update node graph from a file. | ||
243 | * | ||
244 | * @param h FS handle to use | ||
245 | * @param ns namespace to read | ||
246 | * @return update graph, never NULL | ||
247 | */ | ||
248 | static struct GNUNET_FS_UpdateInformationGraph * | ||
249 | read_update_information_graph (struct GNUNET_FS_Handle *h, | ||
250 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns) | ||
251 | { | ||
252 | struct GNUNET_FS_UpdateInformationGraph *uig; | ||
253 | char *fn; | ||
254 | struct GNUNET_BIO_ReadHandle *rh; | ||
255 | unsigned int i; | ||
256 | struct NamespaceUpdateNode *n; | ||
257 | char *uris; | ||
258 | uint32_t count; | ||
259 | char *emsg; | ||
260 | |||
261 | uig = GNUNET_new (struct GNUNET_FS_UpdateInformationGraph); | ||
262 | uig->h = h; | ||
263 | uig->ns = *ns; | ||
264 | fn = get_update_information_directory (h, ns); | ||
265 | if (GNUNET_YES != GNUNET_DISK_file_test (fn)) | ||
266 | { | ||
267 | GNUNET_free (fn); | ||
268 | return uig; | ||
269 | } | ||
270 | rh = GNUNET_BIO_read_open_file (fn); | ||
271 | if (NULL == rh) | ||
272 | { | ||
273 | GNUNET_free (fn); | ||
274 | return uig; | ||
275 | } | ||
276 | if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "fs-namespace-count", | ||
277 | (int32_t *) &count)) | ||
278 | { | ||
279 | GNUNET_break (0); | ||
280 | goto END; | ||
281 | } | ||
282 | if (count > 1024 * 1024) | ||
283 | { | ||
284 | GNUNET_break (0); | ||
285 | goto END; | ||
286 | } | ||
287 | if (0 == count) | ||
288 | goto END; | ||
289 | uig->update_nodes = | ||
290 | GNUNET_malloc (count * sizeof(struct NamespaceUpdateNode *)); | ||
291 | |||
292 | for (i = 0; i < count; i++) | ||
293 | { | ||
294 | n = GNUNET_new (struct NamespaceUpdateNode); | ||
295 | struct GNUNET_BIO_ReadSpec rs[] = { | ||
296 | GNUNET_BIO_read_spec_string ("identifier", &n->id, 1024), | ||
297 | GNUNET_FS_read_spec_meta_data ("meta", &n->md), | ||
298 | GNUNET_BIO_read_spec_string ("update-id", &n->update, 1024), | ||
299 | GNUNET_BIO_read_spec_string ("uri", &uris, 1024 * 2), | ||
300 | GNUNET_BIO_read_spec_end (), | ||
301 | }; | ||
302 | if (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) | ||
303 | { | ||
304 | GNUNET_break (0); | ||
305 | GNUNET_free (n->id); | ||
306 | GNUNET_free (n->update); | ||
307 | if (n->md != NULL) | ||
308 | GNUNET_FS_meta_data_destroy (n->md); | ||
309 | GNUNET_free (n); | ||
310 | break; | ||
311 | } | ||
312 | n->uri = GNUNET_FS_uri_parse (uris, &emsg); | ||
313 | GNUNET_free (uris); | ||
314 | if (n->uri == NULL) | ||
315 | { | ||
316 | GNUNET_break (0); | ||
317 | GNUNET_free (emsg); | ||
318 | GNUNET_free (n->id); | ||
319 | GNUNET_free (n->update); | ||
320 | GNUNET_FS_meta_data_destroy (n->md); | ||
321 | GNUNET_free (n); | ||
322 | break; | ||
323 | } | ||
324 | uig->update_nodes[i] = n; | ||
325 | } | ||
326 | uig->update_node_count = i; | ||
327 | END: | ||
328 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
329 | { | ||
330 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
331 | _ ("Failed to read `%s': %s\n"), | ||
332 | fn, | ||
333 | emsg); | ||
334 | GNUNET_free (emsg); | ||
335 | } | ||
336 | GNUNET_free (fn); | ||
337 | return uig; | ||
338 | } | ||
339 | |||
340 | |||
341 | /** | ||
342 | * Context for the SKS publication. | ||
343 | */ | ||
344 | struct GNUNET_FS_PublishSksContext | ||
345 | { | ||
346 | /** | ||
347 | * URI of the new entry in the namespace. | ||
348 | */ | ||
349 | struct GNUNET_FS_Uri *uri; | ||
350 | |||
351 | /** | ||
352 | * Namespace update node to add to namespace on success (or to be | ||
353 | * deleted if publishing failed). | ||
354 | */ | ||
355 | struct NamespaceUpdateNode *nsn; | ||
356 | |||
357 | /** | ||
358 | * Namespace we're publishing to. | ||
359 | */ | ||
360 | struct GNUNET_CRYPTO_EcdsaPrivateKey ns; | ||
361 | |||
362 | /** | ||
363 | * Handle to the datastore. | ||
364 | */ | ||
365 | struct GNUNET_DATASTORE_Handle *dsh; | ||
366 | |||
367 | /** | ||
368 | * Handle to FS. | ||
369 | */ | ||
370 | struct GNUNET_FS_Handle *h; | ||
371 | |||
372 | /** | ||
373 | * Function to call once we're done. | ||
374 | */ | ||
375 | GNUNET_FS_PublishContinuation cont; | ||
376 | |||
377 | /** | ||
378 | * Closure for cont. | ||
379 | */ | ||
380 | void *cont_cls; | ||
381 | |||
382 | /** | ||
383 | * Handle for our UBlock operation request. | ||
384 | */ | ||
385 | struct GNUNET_FS_PublishUblockContext *uc; | ||
386 | }; | ||
387 | |||
388 | |||
389 | /** | ||
390 | * Function called by the UBlock construction with | ||
391 | * the result from the PUT (UBlock) request. | ||
392 | * | ||
393 | * @param cls closure of type "struct GNUNET_FS_PublishSksContext*" | ||
394 | * @param msg error message (or NULL) | ||
395 | */ | ||
396 | static void | ||
397 | sks_publish_cont (void *cls, const char *msg) | ||
398 | { | ||
399 | struct GNUNET_FS_PublishSksContext *psc = cls; | ||
400 | struct GNUNET_FS_UpdateInformationGraph *uig; | ||
401 | |||
402 | psc->uc = NULL; | ||
403 | if (NULL != msg) | ||
404 | { | ||
405 | if (NULL != psc->cont) | ||
406 | psc->cont (psc->cont_cls, NULL, msg); | ||
407 | GNUNET_FS_publish_sks_cancel (psc); | ||
408 | return; | ||
409 | } | ||
410 | if (NULL != psc->nsn) | ||
411 | { | ||
412 | /* FIXME: this can be done much more | ||
413 | * efficiently by simply appending to the | ||
414 | * file and overwriting the 4-byte header */ | ||
415 | uig = read_update_information_graph (psc->h, &psc->ns); | ||
416 | GNUNET_array_append (uig->update_nodes, uig->update_node_count, psc->nsn); | ||
417 | psc->nsn = NULL; | ||
418 | write_update_information_graph (uig); | ||
419 | free_update_information_graph (uig); | ||
420 | } | ||
421 | if (NULL != psc->cont) | ||
422 | psc->cont (psc->cont_cls, psc->uri, NULL); | ||
423 | GNUNET_FS_publish_sks_cancel (psc); | ||
424 | } | ||
425 | |||
426 | |||
427 | struct GNUNET_FS_PublishSksContext * | ||
428 | GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h, | ||
429 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns, | ||
430 | const char *identifier, | ||
431 | const char *update, | ||
432 | const struct GNUNET_FS_MetaData *meta, | ||
433 | const struct GNUNET_FS_Uri *uri, | ||
434 | const struct GNUNET_FS_BlockOptions *bo, | ||
435 | enum GNUNET_FS_PublishOptions options, | ||
436 | GNUNET_FS_PublishContinuation cont, | ||
437 | void *cont_cls) | ||
438 | { | ||
439 | struct GNUNET_FS_PublishSksContext *psc; | ||
440 | struct GNUNET_FS_Uri *sks_uri; | ||
441 | |||
442 | sks_uri = GNUNET_new (struct GNUNET_FS_Uri); | ||
443 | sks_uri->type = GNUNET_FS_URI_SKS; | ||
444 | sks_uri->data.sks.identifier = GNUNET_strdup (identifier); | ||
445 | GNUNET_CRYPTO_ecdsa_key_get_public (ns, &sks_uri->data.sks.ns); | ||
446 | |||
447 | psc = GNUNET_new (struct GNUNET_FS_PublishSksContext); | ||
448 | psc->h = h; | ||
449 | psc->uri = sks_uri; | ||
450 | psc->cont = cont; | ||
451 | psc->cont_cls = cont_cls; | ||
452 | psc->ns = *ns; | ||
453 | if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) | ||
454 | { | ||
455 | psc->dsh = GNUNET_DATASTORE_connect (h->cfg); | ||
456 | if (NULL == psc->dsh) | ||
457 | { | ||
458 | sks_publish_cont (psc, _ ("Failed to connect to datastore.")); | ||
459 | return NULL; | ||
460 | } | ||
461 | } | ||
462 | if (NULL != update) | ||
463 | { | ||
464 | psc->nsn = GNUNET_new (struct NamespaceUpdateNode); | ||
465 | psc->nsn->id = GNUNET_strdup (identifier); | ||
466 | psc->nsn->update = GNUNET_strdup (update); | ||
467 | psc->nsn->md = GNUNET_FS_meta_data_duplicate (meta); | ||
468 | psc->nsn->uri = GNUNET_FS_uri_dup (uri); | ||
469 | } | ||
470 | psc->uc = GNUNET_FS_publish_ublock_ (h, | ||
471 | psc->dsh, | ||
472 | identifier, | ||
473 | update, | ||
474 | ns, | ||
475 | meta, | ||
476 | uri, | ||
477 | bo, | ||
478 | options, | ||
479 | &sks_publish_cont, | ||
480 | psc); | ||
481 | return psc; | ||
482 | } | ||
483 | |||
484 | |||
485 | /** | ||
486 | * Abort the SKS publishing operation. | ||
487 | * | ||
488 | * @param psc context of the operation to abort. | ||
489 | */ | ||
490 | void | ||
491 | GNUNET_FS_publish_sks_cancel (struct GNUNET_FS_PublishSksContext *psc) | ||
492 | { | ||
493 | if (NULL != psc->uc) | ||
494 | { | ||
495 | GNUNET_FS_publish_ublock_cancel_ (psc->uc); | ||
496 | psc->uc = NULL; | ||
497 | } | ||
498 | if (NULL != psc->dsh) | ||
499 | { | ||
500 | GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO); | ||
501 | psc->dsh = NULL; | ||
502 | } | ||
503 | GNUNET_FS_uri_destroy (psc->uri); | ||
504 | if (NULL != psc->nsn) | ||
505 | { | ||
506 | GNUNET_FS_meta_data_destroy (psc->nsn->md); | ||
507 | GNUNET_FS_uri_destroy (psc->nsn->uri); | ||
508 | GNUNET_free (psc->nsn->id); | ||
509 | GNUNET_free (psc->nsn->update); | ||
510 | GNUNET_free (psc->nsn); | ||
511 | } | ||
512 | GNUNET_free (psc); | ||
513 | } | ||
514 | |||
515 | |||
516 | /** | ||
517 | * Closure for 'process_update_node'. | ||
518 | */ | ||
519 | struct ProcessUpdateClosure | ||
520 | { | ||
521 | /** | ||
522 | * Function to call for each node. | ||
523 | */ | ||
524 | GNUNET_FS_IdentifierProcessor ip; | ||
525 | |||
526 | /** | ||
527 | * Closure for 'ip'. | ||
528 | */ | ||
529 | void *ip_cls; | ||
530 | }; | ||
531 | |||
532 | |||
533 | /** | ||
534 | * Call the iterator in the closure for each node. | ||
535 | * | ||
536 | * @param cls closure (of type 'struct ProcessUpdateClosure *') | ||
537 | * @param key current key code | ||
538 | * @param value value in the hash map (of type 'struct NamespaceUpdateNode *') | ||
539 | * @return GNUNET_YES if we should continue to | ||
540 | * iterate, | ||
541 | * GNUNET_NO if not. | ||
542 | */ | ||
543 | static int | ||
544 | process_update_node (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
545 | { | ||
546 | struct ProcessUpdateClosure *pc = cls; | ||
547 | struct NamespaceUpdateNode *nsn = value; | ||
548 | |||
549 | pc->ip (pc->ip_cls, nsn->id, nsn->uri, nsn->md, nsn->update); | ||
550 | return GNUNET_YES; | ||
551 | } | ||
552 | |||
553 | |||
554 | /** | ||
555 | * Closure for 'find_trees'. | ||
556 | */ | ||
557 | struct FindTreeClosure | ||
558 | { | ||
559 | /** | ||
560 | * UIG we are operating on. | ||
561 | */ | ||
562 | struct GNUNET_FS_UpdateInformationGraph *uig; | ||
563 | |||
564 | /** | ||
565 | * Array with 'head's of TREEs. | ||
566 | */ | ||
567 | struct NamespaceUpdateNode **tree_array; | ||
568 | |||
569 | /** | ||
570 | * Size of 'tree_array' | ||
571 | */ | ||
572 | unsigned int tree_array_size; | ||
573 | |||
574 | /** | ||
575 | * Current generational ID used. | ||
576 | */ | ||
577 | unsigned int nug; | ||
578 | |||
579 | /** | ||
580 | * Identifier for the current TREE, or UINT_MAX for none yet. | ||
581 | */ | ||
582 | unsigned int id; | ||
583 | }; | ||
584 | |||
585 | |||
586 | /** | ||
587 | * Find all nodes reachable from the current node (including the | ||
588 | * current node itself). If they are in no tree, add them to the | ||
589 | * current one. If they are the head of another tree, merge the | ||
590 | * trees. If they are in the middle of another tree, let them be. | ||
591 | * We can tell that a node is already in an tree by checking if | ||
592 | * its 'nug' field is set to the current 'nug' value. It is the | ||
593 | * head of an tree if it is in the 'tree_array' under its respective | ||
594 | * 'tree_id'. | ||
595 | * | ||
596 | * In short, we're trying to find the smallest number of tree to | ||
597 | * cover a directed graph. | ||
598 | * | ||
599 | * @param cls closure (of type 'struct FindTreeClosure') | ||
600 | * @param key current key code | ||
601 | * @param value value in the hash map | ||
602 | * @return GNUNET_YES if we should continue to | ||
603 | * iterate, | ||
604 | * GNUNET_NO if not. | ||
605 | */ | ||
606 | static int | ||
607 | find_trees (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
608 | { | ||
609 | struct FindTreeClosure *fc = cls; | ||
610 | struct NamespaceUpdateNode *nsn = value; | ||
611 | struct GNUNET_HashCode hc; | ||
612 | |||
613 | if (nsn->nug == fc->nug) | ||
614 | { | ||
615 | if (UINT_MAX == nsn->tree_id) | ||
616 | return GNUNET_YES; /* circular */ | ||
617 | GNUNET_assert (nsn->tree_id < fc->tree_array_size); | ||
618 | if (fc->tree_array[nsn->tree_id] != nsn) | ||
619 | return GNUNET_YES; /* part of "another" (directed) TREE, | ||
620 | * and not root of it, end trace */ | ||
621 | if (nsn->tree_id == fc->id) | ||
622 | return GNUNET_YES; /* that's our own root (can this be?) */ | ||
623 | /* merge existing TREE, we have a root for both */ | ||
624 | fc->tree_array[nsn->tree_id] = NULL; | ||
625 | if (UINT_MAX == fc->id) | ||
626 | fc->id = nsn->tree_id; /* take over ID */ | ||
627 | } | ||
628 | else | ||
629 | { | ||
630 | nsn->nug = fc->nug; | ||
631 | nsn->tree_id = UINT_MAX; /* mark as undef */ | ||
632 | /* trace */ | ||
633 | GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc); | ||
634 | GNUNET_CONTAINER_multihashmap_get_multiple (fc->uig->update_map, | ||
635 | &hc, | ||
636 | &find_trees, | ||
637 | fc); | ||
638 | } | ||
639 | return GNUNET_YES; | ||
640 | } | ||
641 | |||
642 | |||
643 | /** | ||
644 | * List all of the identifiers in the namespace for which we could | ||
645 | * produce an update. Namespace updates form a graph where each node | ||
646 | * has a name. Each node can have any number of URI/meta-data entries | ||
647 | * which can each be linked to other nodes. Cycles are possible. | ||
648 | * | ||
649 | * Calling this function with "next_id" NULL will cause the library to | ||
650 | * call "ip" with a root for each strongly connected component of the | ||
651 | * graph (a root being a node from which all other nodes in the Tree | ||
652 | * are reachable). | ||
653 | * | ||
654 | * Calling this function with "next_id" being the name of a node will | ||
655 | * cause the library to call "ip" with all children of the node. Note | ||
656 | * that cycles within the final tree are possible (including self-loops). | ||
657 | * I know, odd definition of a tree, but the GUI will display an actual | ||
658 | * tree (GtkTreeView), so that's what counts for the term here. | ||
659 | * | ||
660 | * @param h fs handle to use | ||
661 | * @param ns namespace to inspect for updateable content | ||
662 | * @param next_id ID to look for; use NULL to look for tree roots | ||
663 | * @param ip function to call on each updateable identifier | ||
664 | * @param ip_cls closure for ip | ||
665 | */ | ||
666 | void | ||
667 | GNUNET_FS_namespace_list_updateable ( | ||
668 | struct GNUNET_FS_Handle *h, | ||
669 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns, | ||
670 | const char *next_id, | ||
671 | GNUNET_FS_IdentifierProcessor ip, | ||
672 | void *ip_cls) | ||
673 | { | ||
674 | unsigned int i; | ||
675 | unsigned int nug; | ||
676 | struct GNUNET_HashCode hc; | ||
677 | struct NamespaceUpdateNode *nsn; | ||
678 | struct ProcessUpdateClosure pc; | ||
679 | struct FindTreeClosure fc; | ||
680 | struct GNUNET_FS_UpdateInformationGraph *uig; | ||
681 | |||
682 | uig = read_update_information_graph (h, ns); | ||
683 | if (NULL == uig->update_nodes) | ||
684 | { | ||
685 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
686 | "No updateable nodes found for ID `%s'\n", | ||
687 | next_id); | ||
688 | free_update_information_graph (uig); | ||
689 | return; /* no nodes */ | ||
690 | } | ||
691 | uig->update_map = | ||
692 | GNUNET_CONTAINER_multihashmap_create (2 + 3 * uig->update_node_count / 4, | ||
693 | GNUNET_NO); | ||
694 | for (i = 0; i < uig->update_node_count; i++) | ||
695 | { | ||
696 | nsn = uig->update_nodes[i]; | ||
697 | GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc); | ||
698 | GNUNET_CONTAINER_multihashmap_put ( | ||
699 | uig->update_map, | ||
700 | &hc, | ||
701 | nsn, | ||
702 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
703 | } | ||
704 | if (NULL != next_id) | ||
705 | { | ||
706 | GNUNET_CRYPTO_hash (next_id, strlen (next_id), &hc); | ||
707 | pc.ip = ip; | ||
708 | pc.ip_cls = ip_cls; | ||
709 | GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, | ||
710 | &hc, | ||
711 | &process_update_node, | ||
712 | &pc); | ||
713 | free_update_information_graph (uig); | ||
714 | return; | ||
715 | } | ||
716 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
717 | "Calculating TREEs to find roots of update trees\n"); | ||
718 | /* Find heads of TREEs in update graph */ | ||
719 | nug = ++uig->nug_gen; | ||
720 | fc.tree_array = NULL; | ||
721 | fc.tree_array_size = 0; | ||
722 | |||
723 | for (i = 0; i < uig->update_node_count; i++) | ||
724 | { | ||
725 | nsn = uig->update_nodes[i]; | ||
726 | if (nsn->nug == nug) | ||
727 | { | ||
728 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
729 | "TREE of node `%s' is %u\n", | ||
730 | nsn->id, | ||
731 | nsn->nug); | ||
732 | continue; /* already placed in TREE */ | ||
733 | } | ||
734 | GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc); | ||
735 | nsn->nug = nug; | ||
736 | nsn->tree_id = UINT_MAX; | ||
737 | fc.id = UINT_MAX; | ||
738 | fc.nug = nug; | ||
739 | fc.uig = uig; | ||
740 | GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, | ||
741 | &hc, | ||
742 | &find_trees, | ||
743 | &fc); | ||
744 | if (UINT_MAX == fc.id) | ||
745 | { | ||
746 | /* start new TREE */ | ||
747 | for (fc.id = 0; fc.id < fc.tree_array_size; fc.id++) | ||
748 | { | ||
749 | if (NULL == fc.tree_array[fc.id]) | ||
750 | { | ||
751 | fc.tree_array[fc.id] = nsn; | ||
752 | nsn->tree_id = fc.id; | ||
753 | break; | ||
754 | } | ||
755 | } | ||
756 | if (fc.id == fc.tree_array_size) | ||
757 | { | ||
758 | GNUNET_array_append (fc.tree_array, fc.tree_array_size, nsn); | ||
759 | nsn->tree_id = fc.id; | ||
760 | } | ||
761 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
762 | "Starting new TREE %u with node `%s'\n", | ||
763 | nsn->tree_id, | ||
764 | nsn->id); | ||
765 | /* put all nodes with same identifier into this TREE */ | ||
766 | GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc); | ||
767 | fc.id = nsn->tree_id; | ||
768 | fc.nug = nug; | ||
769 | fc.uig = uig; | ||
770 | GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, | ||
771 | &hc, | ||
772 | &find_trees, | ||
773 | &fc); | ||
774 | } | ||
775 | else | ||
776 | { | ||
777 | /* make head of TREE "id" */ | ||
778 | fc.tree_array[fc.id] = nsn; | ||
779 | nsn->tree_id = fc.id; | ||
780 | } | ||
781 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
782 | "TREE of node `%s' is %u\n", | ||
783 | nsn->id, | ||
784 | fc.id); | ||
785 | } | ||
786 | for (i = 0; i < fc.tree_array_size; i++) | ||
787 | { | ||
788 | nsn = fc.tree_array[i]; | ||
789 | if (NULL != nsn) | ||
790 | { | ||
791 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
792 | "Root of TREE %u is node `%s'\n", | ||
793 | i, | ||
794 | nsn->id); | ||
795 | ip (ip_cls, nsn->id, nsn->uri, nsn->md, nsn->update); | ||
796 | } | ||
797 | } | ||
798 | GNUNET_array_grow (fc.tree_array, fc.tree_array_size, 0); | ||
799 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Done processing TREEs\n"); | ||
800 | free_update_information_graph (uig); | ||
801 | } | ||
802 | |||
803 | |||
804 | /* end of fs_namespace.c */ | ||
diff --git a/src/fs/fs_publish.c b/src/fs/fs_publish.c deleted file mode 100644 index d1662c78b..000000000 --- a/src/fs/fs_publish.c +++ /dev/null | |||
@@ -1,1625 +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 | |||
32 | #include "gnunet_fs_service.h" | ||
33 | #include "fs_api.h" | ||
34 | #include "fs_tree.h" | ||
35 | |||
36 | |||
37 | /** | ||
38 | * Fill in all of the generic fields for | ||
39 | * a publish event and call the callback. | ||
40 | * | ||
41 | * @param pi structure to fill in | ||
42 | * @param pc overall publishing context | ||
43 | * @param p file information for the file being published | ||
44 | * @param offset where in the file are we so far | ||
45 | * @return value returned from callback | ||
46 | */ | ||
47 | void * | ||
48 | GNUNET_FS_publish_make_status_ (struct GNUNET_FS_ProgressInfo *pi, | ||
49 | struct GNUNET_FS_PublishContext *pc, | ||
50 | const struct GNUNET_FS_FileInformation *p, | ||
51 | uint64_t offset) | ||
52 | { | ||
53 | pi->value.publish.pc = pc; | ||
54 | pi->value.publish.fi = p; | ||
55 | pi->value.publish.cctx = p->client_info; | ||
56 | pi->value.publish.pctx = (NULL == p->dir) ? NULL : p->dir->client_info; | ||
57 | pi->value.publish.filename = p->filename; | ||
58 | pi->value.publish.size = | ||
59 | (GNUNET_YES == p->is_directory) ? p->data.dir.dir_size : | ||
60 | p->data.file.file_size; | ||
61 | pi->value.publish.eta = | ||
62 | GNUNET_TIME_calculate_eta (p->start_time, offset, | ||
63 | pi->value.publish.size); | ||
64 | pi->value.publish.completed = offset; | ||
65 | pi->value.publish.duration = | ||
66 | GNUNET_TIME_absolute_get_duration (p->start_time); | ||
67 | pi->value.publish.anonymity = p->bo.anonymity_level; | ||
68 | pi->fsh = pc->h; | ||
69 | return pc->h->upcb (pc->h->upcb_cls, pi); | ||
70 | } | ||
71 | |||
72 | |||
73 | /** | ||
74 | * Cleanup the publish context, we're done with it. | ||
75 | * | ||
76 | * @param pc struct to clean up | ||
77 | */ | ||
78 | static void | ||
79 | publish_cleanup (struct GNUNET_FS_PublishContext *pc) | ||
80 | { | ||
81 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
82 | "Cleaning up publish context (done!)\n"); | ||
83 | if (NULL != pc->fhc) | ||
84 | { | ||
85 | GNUNET_CRYPTO_hash_file_cancel (pc->fhc); | ||
86 | pc->fhc = NULL; | ||
87 | } | ||
88 | GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL); | ||
89 | GNUNET_free (pc->nid); | ||
90 | GNUNET_free (pc->nuid); | ||
91 | GNUNET_free (pc->serialization); | ||
92 | if (NULL != pc->dsh) | ||
93 | { | ||
94 | GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO); | ||
95 | pc->dsh = NULL; | ||
96 | } | ||
97 | if (NULL != pc->mq) | ||
98 | { | ||
99 | GNUNET_MQ_destroy (pc->mq); | ||
100 | pc->mq = NULL; | ||
101 | } | ||
102 | GNUNET_assert (NULL == pc->upload_task); | ||
103 | GNUNET_free (pc); | ||
104 | } | ||
105 | |||
106 | |||
107 | /** | ||
108 | * Function called by the datastore API with | ||
109 | * the result from the PUT request. | ||
110 | * | ||
111 | * @param cls the `struct GNUNET_FS_PublishContext *` | ||
112 | * @param success #GNUNET_OK on success | ||
113 | * @param min_expiration minimum expiration time required for content to be stored | ||
114 | * @param msg error message (or NULL) | ||
115 | */ | ||
116 | static void | ||
117 | ds_put_cont (void *cls, | ||
118 | int success, | ||
119 | struct GNUNET_TIME_Absolute min_expiration, | ||
120 | const char *msg) | ||
121 | { | ||
122 | struct GNUNET_FS_PublishContext *pc = cls; | ||
123 | struct GNUNET_FS_ProgressInfo pi; | ||
124 | |||
125 | pc->qre = NULL; | ||
126 | if (GNUNET_SYSERR == success) | ||
127 | { | ||
128 | GNUNET_asprintf (&pc->fi_pos->emsg, | ||
129 | _ ("Publishing failed: %s"), | ||
130 | msg); | ||
131 | pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR; | ||
132 | pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL; | ||
133 | pi.value.publish.specifics.error.message = pc->fi_pos->emsg; | ||
134 | pc->fi_pos->client_info = | ||
135 | GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi_pos, 0); | ||
136 | if ((GNUNET_YES != pc->fi_pos->is_directory) && | ||
137 | (NULL != pc->fi_pos->filename) && | ||
138 | (GNUNET_YES == pc->any_done) && | ||
139 | (GNUNET_YES == pc->fi_pos->data.file.do_index)) | ||
140 | { | ||
141 | /* run unindex to clean up */ | ||
142 | GNUNET_FS_unindex_start (pc->h, | ||
143 | pc->fi_pos->filename, | ||
144 | NULL); | ||
145 | } | ||
146 | return; | ||
147 | } | ||
148 | pc->any_done = GNUNET_YES; | ||
149 | GNUNET_assert (NULL == pc->upload_task); | ||
150 | pc->upload_task = | ||
151 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, | ||
152 | &GNUNET_FS_publish_main_, pc); | ||
153 | } | ||
154 | |||
155 | |||
156 | /** | ||
157 | * Generate the callback that signals clients | ||
158 | * that a file (or directory) has been completely | ||
159 | * published. | ||
160 | * | ||
161 | * @param p the completed upload | ||
162 | * @param pc context of the publication | ||
163 | */ | ||
164 | static void | ||
165 | signal_publish_completion (struct GNUNET_FS_FileInformation *p, | ||
166 | struct GNUNET_FS_PublishContext *pc) | ||
167 | { | ||
168 | struct GNUNET_FS_ProgressInfo pi; | ||
169 | |||
170 | pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED; | ||
171 | pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO; | ||
172 | pi.value.publish.specifics.completed.chk_uri = p->chk_uri; | ||
173 | pi.value.publish.specifics.completed.sks_uri = p->sks_uri; | ||
174 | p->client_info = | ||
175 | GNUNET_FS_publish_make_status_ (&pi, pc, p, | ||
176 | p->data.file.file_size); | ||
177 | } | ||
178 | |||
179 | |||
180 | /** | ||
181 | * Generate the callback that signals clients | ||
182 | * that a file (or directory) has encountered | ||
183 | * a problem during publication. | ||
184 | * | ||
185 | * @param p the upload that had trouble | ||
186 | * @param pc context of the publication | ||
187 | * @param emsg error message | ||
188 | */ | ||
189 | static void | ||
190 | signal_publish_error (struct GNUNET_FS_FileInformation *p, | ||
191 | struct GNUNET_FS_PublishContext *pc, | ||
192 | const char *emsg) | ||
193 | { | ||
194 | struct GNUNET_FS_ProgressInfo pi; | ||
195 | |||
196 | p->emsg = GNUNET_strdup (emsg); | ||
197 | pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR; | ||
198 | pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL; | ||
199 | pi.value.publish.specifics.error.message = emsg; | ||
200 | p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0); | ||
201 | if ((p->is_directory != GNUNET_YES) && | ||
202 | (NULL != p->filename) && | ||
203 | (GNUNET_YES == pc->any_done) && | ||
204 | (p->data.file.do_index == GNUNET_YES)) | ||
205 | { | ||
206 | /* run unindex to clean up */ | ||
207 | GNUNET_FS_unindex_start (pc->h, | ||
208 | p->filename, | ||
209 | NULL); | ||
210 | } | ||
211 | } | ||
212 | |||
213 | |||
214 | /** | ||
215 | * Datastore returns from reservation cancel request. | ||
216 | * | ||
217 | * @param cls the `struct GNUNET_FS_PublishContext *` | ||
218 | * @param success success code (not used) | ||
219 | * @param min_expiration minimum expiration time required for content to be stored | ||
220 | * @param msg error message (typically NULL, not used) | ||
221 | */ | ||
222 | static void | ||
223 | finish_release_reserve (void *cls, int success, | ||
224 | struct GNUNET_TIME_Absolute min_expiration, | ||
225 | const char *msg) | ||
226 | { | ||
227 | struct GNUNET_FS_PublishContext *pc = cls; | ||
228 | |||
229 | pc->qre = NULL; | ||
230 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
231 | "Releasing reserve done!\n"); | ||
232 | signal_publish_completion (pc->fi, pc); | ||
233 | pc->all_done = GNUNET_YES; | ||
234 | GNUNET_FS_publish_sync_ (pc); | ||
235 | } | ||
236 | |||
237 | |||
238 | /** | ||
239 | * We've finished publishing the SBlock as part of a larger upload. | ||
240 | * Check the result and complete the larger upload. | ||
241 | * | ||
242 | * @param cls the `struct GNUNET_FS_PublishContext *` of the larger upload | ||
243 | * @param uri URI of the published SBlock | ||
244 | * @param emsg NULL on success, otherwise error message | ||
245 | */ | ||
246 | static void | ||
247 | publish_sblocks_cont (void *cls, | ||
248 | const struct GNUNET_FS_Uri *uri, | ||
249 | const char *emsg) | ||
250 | { | ||
251 | struct GNUNET_FS_PublishContext *pc = cls; | ||
252 | |||
253 | pc->sks_pc = NULL; | ||
254 | if (NULL != emsg) | ||
255 | { | ||
256 | signal_publish_error (pc->fi, pc, emsg); | ||
257 | GNUNET_FS_publish_sync_ (pc); | ||
258 | return; | ||
259 | } | ||
260 | if (NULL != uri) | ||
261 | { | ||
262 | /* sks publication, remember namespace URI */ | ||
263 | pc->fi->sks_uri = GNUNET_FS_uri_dup (uri); | ||
264 | } | ||
265 | GNUNET_assert (pc->qre == NULL); | ||
266 | if ((pc->dsh != NULL) && (pc->rid != 0)) | ||
267 | { | ||
268 | pc->qre = | ||
269 | GNUNET_DATASTORE_release_reserve (pc->dsh, pc->rid, UINT_MAX, UINT_MAX, | ||
270 | &finish_release_reserve, pc); | ||
271 | } | ||
272 | else | ||
273 | { | ||
274 | finish_release_reserve (pc, GNUNET_OK, GNUNET_TIME_UNIT_ZERO_ABS, NULL); | ||
275 | } | ||
276 | } | ||
277 | |||
278 | |||
279 | /** | ||
280 | * We are almost done publishing the structure, | ||
281 | * add SBlocks (if needed). | ||
282 | * | ||
283 | * @param pc overall upload data | ||
284 | */ | ||
285 | static void | ||
286 | publish_sblock (struct GNUNET_FS_PublishContext *pc) | ||
287 | { | ||
288 | if (NULL != pc->ns) | ||
289 | pc->sks_pc = GNUNET_FS_publish_sks (pc->h, | ||
290 | pc->ns, | ||
291 | pc->nid, | ||
292 | pc->nuid, | ||
293 | pc->fi->meta, | ||
294 | pc->fi->chk_uri, | ||
295 | &pc->fi->bo, | ||
296 | pc->options, | ||
297 | &publish_sblocks_cont, pc); | ||
298 | else | ||
299 | publish_sblocks_cont (pc, NULL, NULL); | ||
300 | } | ||
301 | |||
302 | |||
303 | /** | ||
304 | * We've finished publishing a KBlock as part of a larger upload. | ||
305 | * Check the result and continue the larger upload. | ||
306 | * | ||
307 | * @param cls the `struct GNUNET_FS_PublishContext *` | ||
308 | * of the larger upload | ||
309 | * @param uri URI of the published blocks | ||
310 | * @param emsg NULL on success, otherwise error message | ||
311 | */ | ||
312 | static void | ||
313 | publish_kblocks_cont (void *cls, | ||
314 | const struct GNUNET_FS_Uri *uri, | ||
315 | const char *emsg) | ||
316 | { | ||
317 | struct GNUNET_FS_PublishContext *pc = cls; | ||
318 | struct GNUNET_FS_FileInformation *p = pc->fi_pos; | ||
319 | |||
320 | pc->ksk_pc = NULL; | ||
321 | if (NULL != emsg) | ||
322 | { | ||
323 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
324 | "Error uploading KSK blocks: %s\n", | ||
325 | emsg); | ||
326 | signal_publish_error (p, pc, emsg); | ||
327 | GNUNET_FS_file_information_sync_ (p); | ||
328 | GNUNET_FS_publish_sync_ (pc); | ||
329 | GNUNET_assert (NULL == pc->upload_task); | ||
330 | pc->upload_task = | ||
331 | GNUNET_SCHEDULER_add_with_priority | ||
332 | (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, | ||
333 | &GNUNET_FS_publish_main_, | ||
334 | pc); | ||
335 | return; | ||
336 | } | ||
337 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
338 | "KSK blocks published, moving on to next file\n"); | ||
339 | if (NULL != p->dir) | ||
340 | signal_publish_completion (p, pc); | ||
341 | /* move on to next file */ | ||
342 | if (NULL != p->next) | ||
343 | pc->fi_pos = p->next; | ||
344 | else | ||
345 | pc->fi_pos = p->dir; | ||
346 | GNUNET_FS_publish_sync_ (pc); | ||
347 | GNUNET_assert (NULL == pc->upload_task); | ||
348 | pc->upload_task = | ||
349 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, | ||
350 | &GNUNET_FS_publish_main_, pc); | ||
351 | } | ||
352 | |||
353 | |||
354 | /** | ||
355 | * Function called by the tree encoder to obtain | ||
356 | * a block of plaintext data (for the lowest level | ||
357 | * of the tree). | ||
358 | * | ||
359 | * @param cls our publishing context | ||
360 | * @param offset identifies which block to get | ||
361 | * @param max (maximum) number of bytes to get; returning | ||
362 | * fewer will also cause errors | ||
363 | * @param buf where to copy the plaintext buffer | ||
364 | * @param emsg location to store an error message (on error) | ||
365 | * @return number of bytes copied to buf, 0 on error | ||
366 | */ | ||
367 | static size_t | ||
368 | block_reader (void *cls, | ||
369 | uint64_t offset, | ||
370 | size_t max, | ||
371 | void *buf, | ||
372 | char **emsg) | ||
373 | { | ||
374 | struct GNUNET_FS_PublishContext *pc = cls; | ||
375 | struct GNUNET_FS_FileInformation *p; | ||
376 | const char *dd; | ||
377 | size_t pt_size; | ||
378 | |||
379 | p = pc->fi_pos; | ||
380 | if (GNUNET_YES == p->is_directory) | ||
381 | { | ||
382 | pt_size = GNUNET_MIN (max, p->data.dir.dir_size - offset); | ||
383 | dd = p->data.dir.dir_data; | ||
384 | GNUNET_memcpy (buf, &dd[offset], pt_size); | ||
385 | } | ||
386 | else | ||
387 | { | ||
388 | if (UINT64_MAX == offset) | ||
389 | { | ||
390 | if (&GNUNET_FS_data_reader_file_ == p->data.file.reader) | ||
391 | { | ||
392 | /* force closing the file to avoid keeping too many files open */ | ||
393 | p->data.file.reader (p->data.file.reader_cls, offset, 0, NULL, NULL); | ||
394 | } | ||
395 | return 0; | ||
396 | } | ||
397 | pt_size = GNUNET_MIN (max, p->data.file.file_size - offset); | ||
398 | if (0 == pt_size) | ||
399 | return 0; /* calling reader with pt_size==0 | ||
400 | * might free buf, so don't! */ | ||
401 | if (pt_size != | ||
402 | p->data.file.reader (p->data.file.reader_cls, offset, pt_size, buf, | ||
403 | emsg)) | ||
404 | return 0; | ||
405 | } | ||
406 | return pt_size; | ||
407 | } | ||
408 | |||
409 | |||
410 | /** | ||
411 | * The tree encoder has finished processing a | ||
412 | * file. Call it's finish method and deal with | ||
413 | * the final result. | ||
414 | * | ||
415 | * @param cls our publishing context | ||
416 | */ | ||
417 | static void | ||
418 | encode_cont (void *cls) | ||
419 | { | ||
420 | struct GNUNET_FS_PublishContext *pc = cls; | ||
421 | struct GNUNET_FS_FileInformation *p; | ||
422 | struct GNUNET_FS_ProgressInfo pi; | ||
423 | char *emsg; | ||
424 | uint64_t flen; | ||
425 | |||
426 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
427 | "Finished with tree encoder\n"); | ||
428 | p = pc->fi_pos; | ||
429 | p->chk_uri = GNUNET_FS_tree_encoder_get_uri (p->te); | ||
430 | GNUNET_FS_file_information_sync_ (p); | ||
431 | GNUNET_FS_tree_encoder_finish (p->te, &emsg); | ||
432 | p->te = NULL; | ||
433 | if (NULL != emsg) | ||
434 | { | ||
435 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
436 | "Error during tree walk: %s\n", | ||
437 | emsg); | ||
438 | GNUNET_asprintf (&p->emsg, | ||
439 | _ ("Publishing failed: %s"), | ||
440 | emsg); | ||
441 | GNUNET_free (emsg); | ||
442 | pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR; | ||
443 | pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL; | ||
444 | pi.value.publish.specifics.error.message = p->emsg; | ||
445 | p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0); | ||
446 | } | ||
447 | else | ||
448 | { | ||
449 | /* final progress event */ | ||
450 | GNUNET_assert (NULL != p->chk_uri); | ||
451 | flen = GNUNET_FS_uri_chk_get_file_size (p->chk_uri); | ||
452 | pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS; | ||
453 | pi.value.publish.specifics.progress.data = NULL; | ||
454 | pi.value.publish.specifics.progress.offset = flen; | ||
455 | pi.value.publish.specifics.progress.data_len = 0; | ||
456 | pi.value.publish.specifics.progress.depth = GNUNET_FS_compute_depth (flen); | ||
457 | p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, flen); | ||
458 | } | ||
459 | /* continue with main */ /* continue with main */ | ||
460 | GNUNET_assert (NULL == pc->upload_task); | ||
461 | pc->upload_task = | ||
462 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, | ||
463 | &GNUNET_FS_publish_main_, pc); | ||
464 | } | ||
465 | |||
466 | |||
467 | /** | ||
468 | * Function called asking for the current (encoded) | ||
469 | * block to be processed. After processing the | ||
470 | * client should either call #GNUNET_FS_tree_encoder_next | ||
471 | * or (on error) #GNUNET_FS_tree_encoder_finish. | ||
472 | * | ||
473 | * @param cls closure | ||
474 | * @param chk content hash key for the block | ||
475 | * @param offset offset of the block in the file | ||
476 | * @param depth depth of the block in the file, 0 for DBLOCK | ||
477 | * @param type type of the block (IBLOCK or DBLOCK) | ||
478 | * @param block the (encrypted) block | ||
479 | * @param block_size size of @a block (in bytes) | ||
480 | */ | ||
481 | static void | ||
482 | block_proc (void *cls, | ||
483 | const struct ContentHashKey *chk, | ||
484 | uint64_t offset, | ||
485 | unsigned int depth, | ||
486 | enum GNUNET_BLOCK_Type type, | ||
487 | const void *block, | ||
488 | uint16_t block_size) | ||
489 | { | ||
490 | struct GNUNET_FS_PublishContext *pc = cls; | ||
491 | struct GNUNET_FS_FileInformation *p; | ||
492 | struct OnDemandBlock odb; | ||
493 | |||
494 | p = pc->fi_pos; | ||
495 | if (NULL == pc->dsh) | ||
496 | { | ||
497 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
498 | "Waiting for datastore connection\n"); | ||
499 | GNUNET_assert (NULL == pc->upload_task); | ||
500 | pc->upload_task = | ||
501 | GNUNET_SCHEDULER_add_with_priority | ||
502 | (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, pc); | ||
503 | return; | ||
504 | } | ||
505 | |||
506 | if ((GNUNET_YES != p->is_directory) && | ||
507 | (GNUNET_YES == p->data.file.do_index) && | ||
508 | (GNUNET_BLOCK_TYPE_FS_DBLOCK == type)) | ||
509 | { | ||
510 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
511 | "Indexing block `%s' for offset %llu with index size %u\n", | ||
512 | GNUNET_h2s (&chk->query), | ||
513 | (unsigned long long) offset, | ||
514 | (unsigned int) sizeof(struct OnDemandBlock)); | ||
515 | odb.offset = GNUNET_htonll (offset); | ||
516 | odb.file_id = p->data.file.file_id; | ||
517 | GNUNET_assert (pc->qre == NULL); | ||
518 | pc->qre = | ||
519 | GNUNET_DATASTORE_put (pc->dsh, | ||
520 | (p->is_directory == GNUNET_YES) ? 0 : pc->rid, | ||
521 | &chk->query, | ||
522 | sizeof(struct OnDemandBlock), | ||
523 | &odb, | ||
524 | GNUNET_BLOCK_TYPE_FS_ONDEMAND, | ||
525 | p->bo.content_priority, | ||
526 | p->bo.anonymity_level, | ||
527 | p->bo.replication_level, | ||
528 | p->bo.expiration_time, | ||
529 | -2, 1, | ||
530 | &ds_put_cont, pc); | ||
531 | return; | ||
532 | } | ||
533 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
534 | "Publishing block `%s' for offset %llu with size %u\n", | ||
535 | GNUNET_h2s (&chk->query), | ||
536 | (unsigned long long) offset, | ||
537 | (unsigned int) block_size); | ||
538 | GNUNET_assert (pc->qre == NULL); | ||
539 | pc->qre = | ||
540 | GNUNET_DATASTORE_put (pc->dsh, (p->is_directory == GNUNET_YES) ? 0 : | ||
541 | pc->rid, | ||
542 | &chk->query, | ||
543 | block_size, | ||
544 | block, | ||
545 | type, | ||
546 | p->bo.content_priority, | ||
547 | p->bo.anonymity_level, | ||
548 | p->bo.replication_level, | ||
549 | p->bo.expiration_time, | ||
550 | -2, 1, | ||
551 | &ds_put_cont, | ||
552 | pc); | ||
553 | } | ||
554 | |||
555 | |||
556 | /** | ||
557 | * Function called with information about our | ||
558 | * progress in computing the tree encoding. | ||
559 | * | ||
560 | * @param cls closure | ||
561 | * @param offset where are we in the file | ||
562 | * @param pt_block plaintext of the currently processed block | ||
563 | * @param pt_size size of @a pt_block | ||
564 | * @param depth depth of the block in the tree, 0 for DBLOCK | ||
565 | */ | ||
566 | static void | ||
567 | progress_proc (void *cls, uint64_t offset, | ||
568 | const void *pt_block, | ||
569 | size_t pt_size, | ||
570 | unsigned int depth) | ||
571 | { | ||
572 | struct GNUNET_FS_PublishContext *pc = cls; | ||
573 | struct GNUNET_FS_FileInformation *p; | ||
574 | struct GNUNET_FS_FileInformation *par; | ||
575 | struct GNUNET_FS_ProgressInfo pi; | ||
576 | |||
577 | p = pc->fi_pos; | ||
578 | pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS; | ||
579 | pi.value.publish.specifics.progress.data = pt_block; | ||
580 | pi.value.publish.specifics.progress.offset = offset; | ||
581 | pi.value.publish.specifics.progress.data_len = pt_size; | ||
582 | pi.value.publish.specifics.progress.depth = depth; | ||
583 | p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, offset); | ||
584 | if ((0 != depth) || | ||
585 | (GNUNET_YES == p->is_directory)) | ||
586 | return; | ||
587 | while (NULL != (par = p->dir)) | ||
588 | { | ||
589 | p = par; | ||
590 | GNUNET_assert (GNUNET_YES == par->is_directory); | ||
591 | p->data.dir.contents_completed += pt_size; | ||
592 | pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY; | ||
593 | pi.value.publish.specifics.progress_directory.completed = | ||
594 | p->data.dir.contents_completed; | ||
595 | pi.value.publish.specifics.progress_directory.total = | ||
596 | p->data.dir.contents_size; | ||
597 | pi.value.publish.specifics.progress_directory.eta = | ||
598 | GNUNET_TIME_calculate_eta (p->start_time, | ||
599 | p | ||
600 | ->data.dir.contents_completed, | ||
601 | p | ||
602 | ->data.dir.contents_size); | ||
603 | p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0); | ||
604 | } | ||
605 | } | ||
606 | |||
607 | |||
608 | /** | ||
609 | * We are uploading a file or directory; load (if necessary) the next | ||
610 | * block into memory, encrypt it and send it to the FS service. Then | ||
611 | * continue with the main task. | ||
612 | * | ||
613 | * @param pc overall upload data | ||
614 | */ | ||
615 | static void | ||
616 | publish_content (struct GNUNET_FS_PublishContext *pc) | ||
617 | { | ||
618 | struct GNUNET_FS_FileInformation *p; | ||
619 | char *emsg; | ||
620 | struct GNUNET_FS_DirectoryBuilder *db; | ||
621 | struct GNUNET_FS_FileInformation *dirpos; | ||
622 | void *raw_data; | ||
623 | uint64_t size; | ||
624 | |||
625 | p = pc->fi_pos; | ||
626 | GNUNET_assert (NULL != p); | ||
627 | if (NULL == p->te) | ||
628 | { | ||
629 | if (GNUNET_YES == p->is_directory) | ||
630 | { | ||
631 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating directory\n"); | ||
632 | db = GNUNET_FS_directory_builder_create (p->meta); | ||
633 | dirpos = p->data.dir.entries; | ||
634 | while (NULL != dirpos) | ||
635 | { | ||
636 | if (GNUNET_YES == dirpos->is_directory) | ||
637 | { | ||
638 | raw_data = dirpos->data.dir.dir_data; | ||
639 | dirpos->data.dir.dir_data = NULL; | ||
640 | } | ||
641 | else | ||
642 | { | ||
643 | raw_data = NULL; | ||
644 | if ((dirpos->data.file.file_size < MAX_INLINE_SIZE) && | ||
645 | (dirpos->data.file.file_size > 0)) | ||
646 | { | ||
647 | raw_data = GNUNET_malloc (dirpos->data.file.file_size); | ||
648 | emsg = NULL; | ||
649 | if (dirpos->data.file.file_size != | ||
650 | dirpos->data.file.reader (dirpos->data.file.reader_cls, 0, | ||
651 | dirpos->data.file.file_size, raw_data, | ||
652 | &emsg)) | ||
653 | { | ||
654 | GNUNET_free (emsg); | ||
655 | GNUNET_free (raw_data); | ||
656 | raw_data = NULL; | ||
657 | } | ||
658 | dirpos->data.file.reader (dirpos->data.file.reader_cls, UINT64_MAX, | ||
659 | 0, 0, NULL); | ||
660 | } | ||
661 | } | ||
662 | GNUNET_FS_directory_builder_add (db, dirpos->chk_uri, dirpos->meta, | ||
663 | raw_data); | ||
664 | GNUNET_free (raw_data); | ||
665 | dirpos = dirpos->next; | ||
666 | } | ||
667 | GNUNET_free (p->data.dir.dir_data); | ||
668 | p->data.dir.dir_data = NULL; | ||
669 | p->data.dir.dir_size = 0; | ||
670 | GNUNET_FS_directory_builder_finish (db, &p->data.dir.dir_size, | ||
671 | &p->data.dir.dir_data); | ||
672 | GNUNET_FS_file_information_sync_ (p); | ||
673 | } | ||
674 | size = (GNUNET_YES == p->is_directory) ? p->data.dir.dir_size : | ||
675 | p->data.file.file_size; | ||
676 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
677 | "Creating tree encoder\n"); | ||
678 | p->te = | ||
679 | GNUNET_FS_tree_encoder_create (pc->h, size, pc, &block_reader, | ||
680 | &block_proc, &progress_proc, | ||
681 | &encode_cont); | ||
682 | } | ||
683 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
684 | "Processing next block from tree\n"); | ||
685 | GNUNET_FS_tree_encoder_next (p->te); | ||
686 | } | ||
687 | |||
688 | |||
689 | /** | ||
690 | * Check the response from the "fs" service to our 'start index' | ||
691 | * request. | ||
692 | * | ||
693 | * @param cls closure (of type `struct GNUNET_FS_PublishContext *`) | ||
694 | * @param msg the response we got | ||
695 | */ | ||
696 | static int | ||
697 | check_index_start_failed (void *cls, | ||
698 | const struct GNUNET_MessageHeader *msg) | ||
699 | { | ||
700 | size_t msize = ntohs (msg->size) - sizeof(*msg); | ||
701 | const char *emsg = (const char *) &msg[1]; | ||
702 | |||
703 | if (emsg[msize - 1] != '\0') | ||
704 | { | ||
705 | GNUNET_break (0); | ||
706 | return GNUNET_SYSERR; | ||
707 | } | ||
708 | return GNUNET_OK; | ||
709 | } | ||
710 | |||
711 | |||
712 | /** | ||
713 | * Process the response from the "fs" service to our 'start index' | ||
714 | * request. | ||
715 | * | ||
716 | * @param cls closure (of type `struct GNUNET_FS_PublishContext *`) | ||
717 | * @param msg the response we got | ||
718 | */ | ||
719 | static void | ||
720 | handle_index_start_failed (void *cls, | ||
721 | const struct GNUNET_MessageHeader *msg) | ||
722 | { | ||
723 | struct GNUNET_FS_PublishContext *pc = cls; | ||
724 | struct GNUNET_FS_FileInformation *p; | ||
725 | const char *emsg = (const char *) &msg[1]; | ||
726 | char *msgtxt; | ||
727 | |||
728 | GNUNET_MQ_destroy (pc->mq); | ||
729 | pc->mq = NULL; | ||
730 | p = pc->fi_pos; | ||
731 | GNUNET_asprintf (&msgtxt, | ||
732 | _ ("Can not index file `%s': %s.\n"), | ||
733 | p->filename, | ||
734 | gettext (emsg)); | ||
735 | signal_publish_error (p, | ||
736 | pc, | ||
737 | msgtxt); | ||
738 | GNUNET_free (msgtxt); | ||
739 | GNUNET_FS_file_information_sync_ (p); | ||
740 | GNUNET_FS_publish_sync_ (pc); | ||
741 | } | ||
742 | |||
743 | |||
744 | /** | ||
745 | * Process the response from the "fs" service to our 'start index' | ||
746 | * request. | ||
747 | * | ||
748 | * @param cls closure (of type `struct GNUNET_FS_PublishContext *`) | ||
749 | * @param msg the response we got | ||
750 | */ | ||
751 | static void | ||
752 | handle_index_start_ok (void *cls, | ||
753 | const struct GNUNET_MessageHeader *msg) | ||
754 | { | ||
755 | struct GNUNET_FS_PublishContext *pc = cls; | ||
756 | struct GNUNET_FS_FileInformation *p; | ||
757 | |||
758 | GNUNET_MQ_destroy (pc->mq); | ||
759 | pc->mq = NULL; | ||
760 | p = pc->fi_pos; | ||
761 | p->data.file.index_start_confirmed = GNUNET_YES; | ||
762 | GNUNET_FS_file_information_sync_ (p); | ||
763 | publish_content (pc); | ||
764 | } | ||
765 | |||
766 | |||
767 | /** | ||
768 | * Generic error handler, called with the appropriate error code and | ||
769 | * the same closure specified at the creation of the message queue. | ||
770 | * Not every message queue implementation supports an error handler. | ||
771 | * | ||
772 | * @param cls closure with the `struct GNUNET_FS_PublishContext *` | ||
773 | * @param error error code | ||
774 | */ | ||
775 | static void | ||
776 | index_mq_error_handler (void *cls, | ||
777 | enum GNUNET_MQ_Error error) | ||
778 | { | ||
779 | struct GNUNET_FS_PublishContext *pc = cls; | ||
780 | struct GNUNET_FS_FileInformation *p; | ||
781 | |||
782 | if (NULL != pc->mq) | ||
783 | { | ||
784 | GNUNET_MQ_destroy (pc->mq); | ||
785 | pc->mq = NULL; | ||
786 | } | ||
787 | p = pc->fi_pos; | ||
788 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
789 | _ ("Can not index file `%s': %s. Will try to insert instead.\n"), | ||
790 | p->filename, | ||
791 | _ ("error on index-start request to `fs' service")); | ||
792 | p->data.file.do_index = GNUNET_NO; | ||
793 | GNUNET_FS_file_information_sync_ (p); | ||
794 | publish_content (pc); | ||
795 | } | ||
796 | |||
797 | |||
798 | /** | ||
799 | * Function called once the hash computation over an | ||
800 | * indexed file has completed. | ||
801 | * | ||
802 | * @param cls closure, our publishing context | ||
803 | * @param res resulting hash, NULL on error | ||
804 | */ | ||
805 | static void | ||
806 | hash_for_index_cb (void *cls, | ||
807 | const struct GNUNET_HashCode *res) | ||
808 | { | ||
809 | struct GNUNET_FS_PublishContext *pc = cls; | ||
810 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
811 | GNUNET_MQ_hd_fixed_size (index_start_ok, | ||
812 | GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK, | ||
813 | struct GNUNET_MessageHeader, | ||
814 | pc), | ||
815 | GNUNET_MQ_hd_var_size (index_start_failed, | ||
816 | GNUNET_MESSAGE_TYPE_FS_INDEX_START_FAILED, | ||
817 | struct GNUNET_MessageHeader, | ||
818 | pc), | ||
819 | GNUNET_MQ_handler_end () | ||
820 | }; | ||
821 | struct GNUNET_FS_FileInformation *p; | ||
822 | struct GNUNET_MQ_Envelope *env; | ||
823 | struct IndexStartMessage *ism; | ||
824 | size_t slen; | ||
825 | uint64_t dev; | ||
826 | uint64_t ino; | ||
827 | char *fn; | ||
828 | |||
829 | pc->fhc = NULL; | ||
830 | p = pc->fi_pos; | ||
831 | if (NULL == res) | ||
832 | { | ||
833 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
834 | _ ( | ||
835 | "Can not index file `%s': %s. Will try to insert instead.\n"), | ||
836 | p->filename, | ||
837 | _ ("failed to compute hash")); | ||
838 | p->data.file.do_index = GNUNET_NO; | ||
839 | GNUNET_FS_file_information_sync_ (p); | ||
840 | publish_content (pc); | ||
841 | return; | ||
842 | } | ||
843 | if (GNUNET_YES == p->data.file.index_start_confirmed) | ||
844 | { | ||
845 | publish_content (pc); | ||
846 | return; | ||
847 | } | ||
848 | fn = GNUNET_STRINGS_filename_expand (p->filename); | ||
849 | GNUNET_assert (fn != NULL); | ||
850 | slen = strlen (fn) + 1; | ||
851 | if (slen >= | ||
852 | GNUNET_MAX_MESSAGE_SIZE - sizeof(struct IndexStartMessage)) | ||
853 | { | ||
854 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
855 | _ | ||
856 | ("Can not index file `%s': %s. Will try to insert instead.\n"), | ||
857 | fn, _ ("filename too long")); | ||
858 | GNUNET_free (fn); | ||
859 | p->data.file.do_index = GNUNET_NO; | ||
860 | GNUNET_FS_file_information_sync_ (p); | ||
861 | publish_content (pc); | ||
862 | return; | ||
863 | } | ||
864 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
865 | "Hash of indexed file `%s' is `%s'\n", | ||
866 | p->filename, | ||
867 | GNUNET_h2s (res)); | ||
868 | if (0 != (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) | ||
869 | { | ||
870 | p->data.file.file_id = *res; | ||
871 | p->data.file.have_hash = GNUNET_YES; | ||
872 | p->data.file.index_start_confirmed = GNUNET_YES; | ||
873 | GNUNET_FS_file_information_sync_ (p); | ||
874 | publish_content (pc); | ||
875 | GNUNET_free (fn); | ||
876 | return; | ||
877 | } | ||
878 | pc->mq = GNUNET_CLIENT_connect (pc->h->cfg, | ||
879 | "fs", | ||
880 | handlers, | ||
881 | &index_mq_error_handler, | ||
882 | pc); | ||
883 | if (NULL == pc->mq) | ||
884 | { | ||
885 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
886 | _ ( | ||
887 | "Can not index file `%s': %s. Will try to insert instead.\n"), | ||
888 | p->filename, | ||
889 | _ ("could not connect to `fs' service")); | ||
890 | p->data.file.do_index = GNUNET_NO; | ||
891 | publish_content (pc); | ||
892 | GNUNET_free (fn); | ||
893 | return; | ||
894 | } | ||
895 | if (p->data.file.have_hash != GNUNET_YES) | ||
896 | { | ||
897 | p->data.file.file_id = *res; | ||
898 | p->data.file.have_hash = GNUNET_YES; | ||
899 | GNUNET_FS_file_information_sync_ (p); | ||
900 | } | ||
901 | env = GNUNET_MQ_msg_extra (ism, | ||
902 | slen, | ||
903 | GNUNET_MESSAGE_TYPE_FS_INDEX_START); | ||
904 | if (GNUNET_OK == | ||
905 | GNUNET_DISK_file_get_identifiers (p->filename, | ||
906 | &dev, | ||
907 | &ino)) | ||
908 | { | ||
909 | ism->device = GNUNET_htonll (dev); | ||
910 | ism->inode = GNUNET_htonll (ino); | ||
911 | } | ||
912 | else | ||
913 | { | ||
914 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
915 | _ ("Failed to get file identifiers for `%s'\n"), | ||
916 | p->filename); | ||
917 | } | ||
918 | ism->file_id = *res; | ||
919 | GNUNET_memcpy (&ism[1], | ||
920 | fn, | ||
921 | slen); | ||
922 | GNUNET_free (fn); | ||
923 | GNUNET_MQ_send (pc->mq, | ||
924 | env); | ||
925 | } | ||
926 | |||
927 | |||
928 | /** | ||
929 | * We've computed the CHK/LOC URI, now publish the KSKs (if applicable). | ||
930 | * | ||
931 | * @param pc publishing context to do this for | ||
932 | */ | ||
933 | static void | ||
934 | publish_kblocks (struct GNUNET_FS_PublishContext *pc) | ||
935 | { | ||
936 | struct GNUNET_FS_FileInformation *p; | ||
937 | |||
938 | p = pc->fi_pos; | ||
939 | /* upload of "p" complete, publish KBlocks! */ | ||
940 | if (NULL != p->keywords) | ||
941 | { | ||
942 | pc->ksk_pc = GNUNET_FS_publish_ksk (pc->h, | ||
943 | p->keywords, | ||
944 | p->meta, | ||
945 | p->chk_uri, | ||
946 | &p->bo, | ||
947 | pc->options, | ||
948 | &publish_kblocks_cont, | ||
949 | pc); | ||
950 | } | ||
951 | else | ||
952 | { | ||
953 | publish_kblocks_cont (pc, p->chk_uri, NULL); | ||
954 | } | ||
955 | } | ||
956 | |||
957 | |||
958 | /** | ||
959 | * Process the response from the "fs" service to our LOC sign request. | ||
960 | * | ||
961 | * @param cls closure (of type `struct GNUNET_FS_PublishContext *`) | ||
962 | * @param sig the response we got | ||
963 | */ | ||
964 | static void | ||
965 | handle_signature_response (void *cls, | ||
966 | const struct ResponseLocSignatureMessage *sig) | ||
967 | { | ||
968 | struct GNUNET_FS_PublishContext *pc = cls; | ||
969 | struct GNUNET_FS_FileInformation *p; | ||
970 | |||
971 | p = pc->fi_pos; | ||
972 | p->chk_uri->type = GNUNET_FS_URI_LOC; | ||
973 | /* p->data.loc.fi kept from CHK before */ | ||
974 | p->chk_uri->data.loc.peer = sig->peer; | ||
975 | p->chk_uri->data.loc.expirationTime | ||
976 | = GNUNET_TIME_absolute_ntoh (sig->expiration_time); | ||
977 | p->chk_uri->data.loc.contentSignature = sig->signature; | ||
978 | GNUNET_FS_file_information_sync_ (p); | ||
979 | GNUNET_FS_publish_sync_ (pc); | ||
980 | publish_kblocks (pc); | ||
981 | } | ||
982 | |||
983 | |||
984 | /** | ||
985 | * Generic error handler, called with the appropriate error code and | ||
986 | * the same closure specified at the creation of the message queue. | ||
987 | * Not every message queue implementation supports an error handler. | ||
988 | * | ||
989 | * @param cls closure with the `struct GNUNET_FS_PublishContext *` | ||
990 | * @param error error code | ||
991 | */ | ||
992 | static void | ||
993 | loc_mq_error_handler (void *cls, | ||
994 | enum GNUNET_MQ_Error error) | ||
995 | { | ||
996 | struct GNUNET_FS_PublishContext *pc = cls; | ||
997 | |||
998 | if (NULL != pc->mq) | ||
999 | { | ||
1000 | GNUNET_MQ_destroy (pc->mq); | ||
1001 | pc->mq = NULL; | ||
1002 | } | ||
1003 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1004 | _ ("Can not create LOC URI. Will continue with CHK instead.\n")); | ||
1005 | publish_kblocks (pc); | ||
1006 | } | ||
1007 | |||
1008 | |||
1009 | /** | ||
1010 | * We're publishing without anonymity. Contact the FS service | ||
1011 | * to create a signed LOC URI for further processing, then | ||
1012 | * continue with KSKs. | ||
1013 | * | ||
1014 | * @param pc the publishing context do to this for | ||
1015 | */ | ||
1016 | static void | ||
1017 | create_loc_uri (struct GNUNET_FS_PublishContext *pc) | ||
1018 | { | ||
1019 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
1020 | GNUNET_MQ_hd_fixed_size (signature_response, | ||
1021 | GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGNATURE, | ||
1022 | struct ResponseLocSignatureMessage, | ||
1023 | pc), | ||
1024 | GNUNET_MQ_handler_end () | ||
1025 | }; | ||
1026 | struct GNUNET_MQ_Envelope *env; | ||
1027 | struct RequestLocSignatureMessage *req; | ||
1028 | struct GNUNET_FS_FileInformation *p; | ||
1029 | |||
1030 | if (NULL != pc->mq) | ||
1031 | GNUNET_MQ_destroy (pc->mq); | ||
1032 | pc->mq = GNUNET_CLIENT_connect (pc->h->cfg, | ||
1033 | "fs", | ||
1034 | handlers, | ||
1035 | &loc_mq_error_handler, | ||
1036 | pc); | ||
1037 | if (NULL == pc->mq) | ||
1038 | { | ||
1039 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1040 | _ ( | ||
1041 | "Can not create LOC URI. Will continue with CHK instead.\n")); | ||
1042 | publish_kblocks (pc); | ||
1043 | return; | ||
1044 | } | ||
1045 | p = pc->fi_pos; | ||
1046 | env = GNUNET_MQ_msg (req, | ||
1047 | GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGN); | ||
1048 | req->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT); | ||
1049 | req->expiration_time = GNUNET_TIME_absolute_hton (p->bo.expiration_time); | ||
1050 | req->chk = p->chk_uri->data.chk.chk; | ||
1051 | req->file_length = GNUNET_htonll (p->chk_uri->data.chk.file_length); | ||
1052 | GNUNET_MQ_send (pc->mq, | ||
1053 | env); | ||
1054 | } | ||
1055 | |||
1056 | |||
1057 | /** | ||
1058 | * Main function that performs the upload. | ||
1059 | * | ||
1060 | * @param cls `struct GNUNET_FS_PublishContext *` identifies the upload | ||
1061 | */ | ||
1062 | void | ||
1063 | GNUNET_FS_publish_main_ (void *cls) | ||
1064 | { | ||
1065 | struct GNUNET_FS_PublishContext *pc = cls; | ||
1066 | struct GNUNET_FS_ProgressInfo pi; | ||
1067 | struct GNUNET_FS_FileInformation *p; | ||
1068 | char *fn; | ||
1069 | |||
1070 | pc->upload_task = NULL; | ||
1071 | p = pc->fi_pos; | ||
1072 | if (NULL == p) | ||
1073 | { | ||
1074 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1075 | "Publishing complete, now publishing SKS and KSK blocks.\n"); | ||
1076 | /* upload of entire hierarchy complete, | ||
1077 | * publish namespace entries */ | ||
1078 | GNUNET_FS_publish_sync_ (pc); | ||
1079 | publish_sblock (pc); | ||
1080 | return; | ||
1081 | } | ||
1082 | /* find starting position */ | ||
1083 | while ((GNUNET_YES == p->is_directory) && | ||
1084 | (NULL != p->data.dir.entries) && | ||
1085 | (NULL == p->emsg) && | ||
1086 | (NULL == p->data.dir.entries->chk_uri)) | ||
1087 | { | ||
1088 | p = p->data.dir.entries; | ||
1089 | pc->fi_pos = p; | ||
1090 | GNUNET_FS_publish_sync_ (pc); | ||
1091 | } | ||
1092 | /* abort on error */ | ||
1093 | if (NULL != p->emsg) | ||
1094 | { | ||
1095 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1096 | "Error uploading: %s\n", | ||
1097 | p->emsg); | ||
1098 | /* error with current file, abort all | ||
1099 | * related files as well! */ | ||
1100 | while (NULL != p->dir) | ||
1101 | { | ||
1102 | fn = GNUNET_FS_meta_data_get_by_type (p->meta, | ||
1103 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); | ||
1104 | p = p->dir; | ||
1105 | if (fn != NULL) | ||
1106 | { | ||
1107 | GNUNET_asprintf (&p->emsg, | ||
1108 | _ ("Recursive upload failed at `%s': %s"), | ||
1109 | fn, | ||
1110 | p->emsg); | ||
1111 | GNUNET_free (fn); | ||
1112 | } | ||
1113 | else | ||
1114 | { | ||
1115 | GNUNET_asprintf (&p->emsg, | ||
1116 | _ ("Recursive upload failed: %s"), | ||
1117 | p->emsg); | ||
1118 | } | ||
1119 | pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR; | ||
1120 | pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL; | ||
1121 | pi.value.publish.specifics.error.message = p->emsg; | ||
1122 | p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0); | ||
1123 | } | ||
1124 | pc->all_done = GNUNET_YES; | ||
1125 | GNUNET_FS_publish_sync_ (pc); | ||
1126 | return; | ||
1127 | } | ||
1128 | /* handle completion */ | ||
1129 | if (NULL != p->chk_uri) | ||
1130 | { | ||
1131 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1132 | "File upload complete, now publishing KSK blocks.\n"); | ||
1133 | GNUNET_FS_publish_sync_ (pc); | ||
1134 | |||
1135 | if ((0 == p->bo.anonymity_level) && | ||
1136 | (GNUNET_YES != | ||
1137 | GNUNET_FS_uri_test_loc (p->chk_uri))) | ||
1138 | { | ||
1139 | /* zero anonymity, box CHK URI in LOC URI */ | ||
1140 | create_loc_uri (pc); | ||
1141 | } | ||
1142 | else | ||
1143 | { | ||
1144 | publish_kblocks (pc); | ||
1145 | } | ||
1146 | return; | ||
1147 | } | ||
1148 | if ((GNUNET_YES != p->is_directory) && (p->data.file.do_index)) | ||
1149 | { | ||
1150 | if (NULL == p->filename) | ||
1151 | { | ||
1152 | p->data.file.do_index = GNUNET_NO; | ||
1153 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1154 | _ ( | ||
1155 | "Can not index file `%s': %s. Will try to insert instead.\n"), | ||
1156 | "<no-name>", | ||
1157 | _ ("needs to be an actual file")); | ||
1158 | GNUNET_FS_file_information_sync_ (p); | ||
1159 | publish_content (pc); | ||
1160 | return; | ||
1161 | } | ||
1162 | if (p->data.file.have_hash) | ||
1163 | { | ||
1164 | hash_for_index_cb (pc, &p->data.file.file_id); | ||
1165 | } | ||
1166 | else | ||
1167 | { | ||
1168 | p->start_time = GNUNET_TIME_absolute_get (); | ||
1169 | pc->fhc = | ||
1170 | GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, p->filename, | ||
1171 | HASHING_BLOCKSIZE, &hash_for_index_cb, pc); | ||
1172 | } | ||
1173 | return; | ||
1174 | } | ||
1175 | publish_content (pc); | ||
1176 | } | ||
1177 | |||
1178 | |||
1179 | /** | ||
1180 | * Signal the FS's progress function that we are starting | ||
1181 | * an upload. | ||
1182 | * | ||
1183 | * @param cls closure (of type `struct GNUNET_FS_PublishContext *`) | ||
1184 | * @param fi the entry in the publish-structure | ||
1185 | * @param length length of the file or directory | ||
1186 | * @param meta metadata for the file or directory (can be modified) | ||
1187 | * @param uri pointer to the keywords that will be used for this entry (can be modified) | ||
1188 | * @param bo block options | ||
1189 | * @param do_index should we index? | ||
1190 | * @param client_info pointer to client context set upon creation (can be modified) | ||
1191 | * @return #GNUNET_OK to continue (always) | ||
1192 | */ | ||
1193 | static int | ||
1194 | fip_signal_start (void *cls, | ||
1195 | struct GNUNET_FS_FileInformation *fi, | ||
1196 | uint64_t length, | ||
1197 | struct GNUNET_FS_MetaData *meta, | ||
1198 | struct GNUNET_FS_Uri **uri, | ||
1199 | struct GNUNET_FS_BlockOptions *bo, | ||
1200 | int *do_index, | ||
1201 | void **client_info) | ||
1202 | { | ||
1203 | struct GNUNET_FS_PublishContext *pc = cls; | ||
1204 | struct GNUNET_FS_ProgressInfo pi; | ||
1205 | unsigned int kc; | ||
1206 | uint64_t left; | ||
1207 | |||
1208 | if (GNUNET_YES == pc->skip_next_fi_callback) | ||
1209 | { | ||
1210 | pc->skip_next_fi_callback = GNUNET_NO; | ||
1211 | return GNUNET_OK; | ||
1212 | } | ||
1213 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1214 | "Starting publish operation\n"); | ||
1215 | if (*do_index) | ||
1216 | { | ||
1217 | /* space for on-demand blocks */ | ||
1218 | pc->reserve_space += | ||
1219 | ((length + DBLOCK_SIZE | ||
1220 | - 1) / DBLOCK_SIZE) * sizeof(struct OnDemandBlock); | ||
1221 | } | ||
1222 | else | ||
1223 | { | ||
1224 | /* space for DBlocks */ | ||
1225 | pc->reserve_space += length; | ||
1226 | } | ||
1227 | /* entries for IBlocks and DBlocks, space for IBlocks */ | ||
1228 | left = length; | ||
1229 | while (1) | ||
1230 | { | ||
1231 | left = (left + DBLOCK_SIZE - 1) / DBLOCK_SIZE; | ||
1232 | pc->reserve_entries += left; | ||
1233 | if (left <= 1) | ||
1234 | break; | ||
1235 | left = left * sizeof(struct ContentHashKey); | ||
1236 | pc->reserve_space += left; | ||
1237 | } | ||
1238 | pc->reserve_entries++; | ||
1239 | /* entries and space for keywords */ | ||
1240 | if (NULL != *uri) | ||
1241 | { | ||
1242 | kc = GNUNET_FS_uri_ksk_get_keyword_count (*uri); | ||
1243 | pc->reserve_entries += kc; | ||
1244 | pc->reserve_space += GNUNET_MAX_MESSAGE_SIZE * kc; | ||
1245 | } | ||
1246 | pi.status = GNUNET_FS_STATUS_PUBLISH_START; | ||
1247 | *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0); | ||
1248 | GNUNET_FS_file_information_sync_ (fi); | ||
1249 | if ((fi->is_directory) && (fi->dir != NULL)) | ||
1250 | { | ||
1251 | /* We are a directory, and we are not top-level; process entries in directory */ | ||
1252 | pc->skip_next_fi_callback = GNUNET_YES; | ||
1253 | GNUNET_FS_file_information_inspect (fi, &fip_signal_start, pc); | ||
1254 | } | ||
1255 | return GNUNET_OK; | ||
1256 | } | ||
1257 | |||
1258 | |||
1259 | /** | ||
1260 | * Actually signal the FS's progress function that we are suspending | ||
1261 | * an upload. | ||
1262 | * | ||
1263 | * @param fi the entry in the publish-structure | ||
1264 | * @param pc the publish context of which a file is being suspended | ||
1265 | */ | ||
1266 | static void | ||
1267 | suspend_operation (struct GNUNET_FS_FileInformation *fi, | ||
1268 | struct GNUNET_FS_PublishContext *pc) | ||
1269 | { | ||
1270 | struct GNUNET_FS_ProgressInfo pi; | ||
1271 | uint64_t off; | ||
1272 | |||
1273 | if (NULL != pc->ksk_pc) | ||
1274 | { | ||
1275 | GNUNET_FS_publish_ksk_cancel (pc->ksk_pc); | ||
1276 | pc->ksk_pc = NULL; | ||
1277 | } | ||
1278 | if (NULL != pc->sks_pc) | ||
1279 | { | ||
1280 | GNUNET_FS_publish_sks_cancel (pc->sks_pc); | ||
1281 | pc->sks_pc = NULL; | ||
1282 | } | ||
1283 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1284 | "Suspending publish operation\n"); | ||
1285 | GNUNET_free (fi->serialization); | ||
1286 | fi->serialization = NULL; | ||
1287 | off = (NULL == fi->chk_uri) ? 0 : (GNUNET_YES == fi->is_directory) ? | ||
1288 | fi->data.dir.dir_size : fi->data.file.file_size; | ||
1289 | pi.status = GNUNET_FS_STATUS_PUBLISH_SUSPEND; | ||
1290 | GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off)); | ||
1291 | if (NULL != pc->qre) | ||
1292 | { | ||
1293 | GNUNET_DATASTORE_cancel (pc->qre); | ||
1294 | pc->qre = NULL; | ||
1295 | } | ||
1296 | if (NULL != pc->dsh) | ||
1297 | { | ||
1298 | GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO); | ||
1299 | pc->dsh = NULL; | ||
1300 | } | ||
1301 | pc->rid = 0; | ||
1302 | } | ||
1303 | |||
1304 | |||
1305 | /** | ||
1306 | * Signal the FS's progress function that we are suspending | ||
1307 | * an upload. Performs the recursion. | ||
1308 | * | ||
1309 | * @param cls closure (of type `struct GNUNET_FS_PublishContext *`) | ||
1310 | * @param fi the entry in the publish-structure | ||
1311 | * @param length length of the file or directory | ||
1312 | * @param meta metadata for the file or directory (can be modified) | ||
1313 | * @param uri pointer to the keywords that will be used for this entry (can be modified) | ||
1314 | * @param bo block options | ||
1315 | * @param do_index should we index? | ||
1316 | * @param client_info pointer to client context set upon creation (can be modified) | ||
1317 | * @return #GNUNET_OK to continue (always) | ||
1318 | */ | ||
1319 | static int | ||
1320 | fip_signal_suspend (void *cls, | ||
1321 | struct GNUNET_FS_FileInformation *fi, | ||
1322 | uint64_t length, | ||
1323 | struct GNUNET_FS_MetaData *meta, | ||
1324 | struct GNUNET_FS_Uri **uri, | ||
1325 | struct GNUNET_FS_BlockOptions *bo, | ||
1326 | int *do_index, | ||
1327 | void **client_info) | ||
1328 | { | ||
1329 | struct GNUNET_FS_PublishContext *pc = cls; | ||
1330 | |||
1331 | if (GNUNET_YES == pc->skip_next_fi_callback) | ||
1332 | { | ||
1333 | pc->skip_next_fi_callback = GNUNET_NO; | ||
1334 | return GNUNET_OK; | ||
1335 | } | ||
1336 | if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) | ||
1337 | { | ||
1338 | /* process entries in directory */ | ||
1339 | pc->skip_next_fi_callback = GNUNET_YES; | ||
1340 | GNUNET_FS_file_information_inspect (fi, &fip_signal_suspend, pc); | ||
1341 | } | ||
1342 | suspend_operation (fi, pc); | ||
1343 | *client_info = NULL; | ||
1344 | return GNUNET_OK; | ||
1345 | } | ||
1346 | |||
1347 | |||
1348 | /** | ||
1349 | * Create SUSPEND event for the given publish operation | ||
1350 | * and then clean up our state (without stop signal). | ||
1351 | * | ||
1352 | * @param cls the `struct GNUNET_FS_PublishContext` to signal for | ||
1353 | */ | ||
1354 | void | ||
1355 | GNUNET_FS_publish_signal_suspend_ (void *cls) | ||
1356 | { | ||
1357 | struct GNUNET_FS_PublishContext *pc = cls; | ||
1358 | |||
1359 | if (NULL != pc->upload_task) | ||
1360 | { | ||
1361 | GNUNET_SCHEDULER_cancel (pc->upload_task); | ||
1362 | pc->upload_task = NULL; | ||
1363 | } | ||
1364 | pc->skip_next_fi_callback = GNUNET_YES; | ||
1365 | GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_suspend, pc); | ||
1366 | suspend_operation (pc->fi, pc); | ||
1367 | GNUNET_FS_end_top (pc->h, pc->top); | ||
1368 | pc->top = NULL; | ||
1369 | publish_cleanup (pc); | ||
1370 | } | ||
1371 | |||
1372 | |||
1373 | /** | ||
1374 | * We have gotten a reply for our space reservation request. | ||
1375 | * Either fail (insufficient space) or start publishing for good. | ||
1376 | * | ||
1377 | * @param cls the `struct GNUNET_FS_PublishContext *` | ||
1378 | * @param success positive reservation ID on success | ||
1379 | * @param min_expiration minimum expiration time required for content to be stored | ||
1380 | * @param msg error message on error, otherwise NULL | ||
1381 | */ | ||
1382 | static void | ||
1383 | finish_reserve (void *cls, | ||
1384 | int success, | ||
1385 | struct GNUNET_TIME_Absolute min_expiration, | ||
1386 | const char *msg) | ||
1387 | { | ||
1388 | struct GNUNET_FS_PublishContext *pc = cls; | ||
1389 | |||
1390 | pc->qre = NULL; | ||
1391 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1392 | "Reservation complete (%d)!\n", | ||
1393 | success); | ||
1394 | if ((msg != NULL) || (success <= 0)) | ||
1395 | { | ||
1396 | GNUNET_asprintf (&pc->fi->emsg, | ||
1397 | _ ("Datastore failure: %s"), | ||
1398 | msg); | ||
1399 | signal_publish_error (pc->fi, pc, pc->fi->emsg); | ||
1400 | return; | ||
1401 | } | ||
1402 | pc->rid = success; | ||
1403 | GNUNET_assert (NULL == pc->upload_task); | ||
1404 | pc->upload_task = | ||
1405 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, | ||
1406 | &GNUNET_FS_publish_main_, pc); | ||
1407 | } | ||
1408 | |||
1409 | |||
1410 | /** | ||
1411 | * Calculate the total size of all of the files in the directory structure. | ||
1412 | * | ||
1413 | * @param fi file structure to traverse | ||
1414 | */ | ||
1415 | static uint64_t | ||
1416 | compute_contents_size (struct GNUNET_FS_FileInformation *fi) | ||
1417 | { | ||
1418 | struct GNUNET_FS_FileInformation *ent; | ||
1419 | |||
1420 | if (GNUNET_YES != fi->is_directory) | ||
1421 | return fi->data.file.file_size; | ||
1422 | fi->data.dir.contents_size = 0; | ||
1423 | for (ent = fi->data.dir.entries; NULL != ent; ent = ent->next) | ||
1424 | fi->data.dir.contents_size += compute_contents_size (ent); | ||
1425 | return fi->data.dir.contents_size; | ||
1426 | } | ||
1427 | |||
1428 | |||
1429 | /** | ||
1430 | * Publish a file or directory. | ||
1431 | * | ||
1432 | * @param h handle to the file sharing subsystem | ||
1433 | * @param fi information about the file or directory structure to publish | ||
1434 | * @param ns namespace to publish the file in, NULL for no namespace | ||
1435 | * @param nid identifier to use for the published content in the namespace | ||
1436 | * (can be NULL, must be NULL if namespace is NULL) | ||
1437 | * @param nuid update-identifier that will be used for future updates | ||
1438 | * (can be NULL, must be NULL if namespace or nid is NULL) | ||
1439 | * @param options options for the publication | ||
1440 | * @return context that can be used to control the publish operation | ||
1441 | */ | ||
1442 | struct GNUNET_FS_PublishContext * | ||
1443 | GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h, | ||
1444 | struct GNUNET_FS_FileInformation *fi, | ||
1445 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns, | ||
1446 | const char *nid, | ||
1447 | const char *nuid, | ||
1448 | enum GNUNET_FS_PublishOptions options) | ||
1449 | { | ||
1450 | struct GNUNET_FS_PublishContext *ret; | ||
1451 | struct GNUNET_DATASTORE_Handle *dsh; | ||
1452 | |||
1453 | GNUNET_assert (NULL != h); | ||
1454 | compute_contents_size (fi); | ||
1455 | if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) | ||
1456 | { | ||
1457 | dsh = GNUNET_DATASTORE_connect (h->cfg); | ||
1458 | if (NULL == dsh) | ||
1459 | return NULL; | ||
1460 | } | ||
1461 | else | ||
1462 | { | ||
1463 | dsh = NULL; | ||
1464 | } | ||
1465 | ret = GNUNET_new (struct GNUNET_FS_PublishContext); | ||
1466 | ret->dsh = dsh; | ||
1467 | ret->h = h; | ||
1468 | ret->fi = fi; | ||
1469 | if (NULL != ns) | ||
1470 | { | ||
1471 | ret->ns = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey); | ||
1472 | *ret->ns = *ns; | ||
1473 | GNUNET_assert (NULL != nid); | ||
1474 | ret->nid = GNUNET_strdup (nid); | ||
1475 | if (NULL != nuid) | ||
1476 | ret->nuid = GNUNET_strdup (nuid); | ||
1477 | } | ||
1478 | ret->options = options; | ||
1479 | /* signal start */ | ||
1480 | GNUNET_FS_file_information_inspect (ret->fi, &fip_signal_start, ret); | ||
1481 | ret->fi_pos = ret->fi; | ||
1482 | ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, ret); | ||
1483 | GNUNET_FS_publish_sync_ (ret); | ||
1484 | if (NULL != ret->dsh) | ||
1485 | { | ||
1486 | GNUNET_assert (NULL == ret->qre); | ||
1487 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1488 | _ ( | ||
1489 | "Reserving space for %u entries and %llu bytes for publication\n"), | ||
1490 | (unsigned int) ret->reserve_entries, | ||
1491 | (unsigned long long) ret->reserve_space); | ||
1492 | ret->qre = | ||
1493 | GNUNET_DATASTORE_reserve (ret->dsh, ret->reserve_space, | ||
1494 | ret->reserve_entries, | ||
1495 | &finish_reserve, | ||
1496 | ret); | ||
1497 | } | ||
1498 | else | ||
1499 | { | ||
1500 | GNUNET_assert (NULL == ret->upload_task); | ||
1501 | ret->upload_task = | ||
1502 | GNUNET_SCHEDULER_add_with_priority | ||
1503 | (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, ret); | ||
1504 | } | ||
1505 | return ret; | ||
1506 | } | ||
1507 | |||
1508 | |||
1509 | /** | ||
1510 | * Signal the FS's progress function that we are stopping | ||
1511 | * an upload. | ||
1512 | * | ||
1513 | * @param cls closure (of type `struct GNUNET_FS_PublishContext *`) | ||
1514 | * @param fi the entry in the publish-structure | ||
1515 | * @param length length of the file or directory | ||
1516 | * @param meta metadata for the file or directory (can be modified) | ||
1517 | * @param uri pointer to the keywords that will be used for this entry (can be modified) | ||
1518 | * @param bo block options (can be modified) | ||
1519 | * @param do_index should we index? | ||
1520 | * @param client_info pointer to client context set upon creation (can be modified) | ||
1521 | * @return #GNUNET_OK to continue (always) | ||
1522 | */ | ||
1523 | static int | ||
1524 | fip_signal_stop (void *cls, | ||
1525 | struct GNUNET_FS_FileInformation *fi, | ||
1526 | uint64_t length, | ||
1527 | struct GNUNET_FS_MetaData *meta, | ||
1528 | struct GNUNET_FS_Uri **uri, | ||
1529 | struct GNUNET_FS_BlockOptions *bo, | ||
1530 | int *do_index, void **client_info) | ||
1531 | { | ||
1532 | struct GNUNET_FS_PublishContext *pc = cls; | ||
1533 | struct GNUNET_FS_ProgressInfo pi; | ||
1534 | uint64_t off; | ||
1535 | |||
1536 | if (GNUNET_YES == pc->skip_next_fi_callback) | ||
1537 | { | ||
1538 | pc->skip_next_fi_callback = GNUNET_NO; | ||
1539 | return GNUNET_OK; | ||
1540 | } | ||
1541 | if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) | ||
1542 | { | ||
1543 | /* process entries in directory first */ | ||
1544 | pc->skip_next_fi_callback = GNUNET_YES; | ||
1545 | GNUNET_FS_file_information_inspect (fi, &fip_signal_stop, pc); | ||
1546 | } | ||
1547 | if (NULL != fi->serialization) | ||
1548 | { | ||
1549 | GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO, | ||
1550 | fi->serialization); | ||
1551 | GNUNET_free (fi->serialization); | ||
1552 | fi->serialization = NULL; | ||
1553 | } | ||
1554 | off = (fi->chk_uri == NULL) ? 0 : length; | ||
1555 | pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED; | ||
1556 | GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off)); | ||
1557 | *client_info = NULL; | ||
1558 | return GNUNET_OK; | ||
1559 | } | ||
1560 | |||
1561 | |||
1562 | /** | ||
1563 | * Stop an upload. Will abort incomplete uploads (but | ||
1564 | * not remove blocks that have already been published) or | ||
1565 | * simply clean up the state for completed uploads. | ||
1566 | * Must NOT be called from within the event callback! | ||
1567 | * | ||
1568 | * @param pc context for the upload to stop | ||
1569 | */ | ||
1570 | void | ||
1571 | GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *pc) | ||
1572 | { | ||
1573 | struct GNUNET_FS_ProgressInfo pi; | ||
1574 | uint64_t off; | ||
1575 | |||
1576 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1577 | "Publish stop called\n"); | ||
1578 | GNUNET_FS_end_top (pc->h, pc->top); | ||
1579 | if (NULL != pc->ksk_pc) | ||
1580 | { | ||
1581 | GNUNET_FS_publish_ksk_cancel (pc->ksk_pc); | ||
1582 | pc->ksk_pc = NULL; | ||
1583 | } | ||
1584 | if (NULL != pc->sks_pc) | ||
1585 | { | ||
1586 | GNUNET_FS_publish_sks_cancel (pc->sks_pc); | ||
1587 | pc->sks_pc = NULL; | ||
1588 | } | ||
1589 | if (NULL != pc->upload_task) | ||
1590 | { | ||
1591 | GNUNET_SCHEDULER_cancel (pc->upload_task); | ||
1592 | pc->upload_task = NULL; | ||
1593 | } | ||
1594 | pc->skip_next_fi_callback = GNUNET_YES; | ||
1595 | GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_stop, pc); | ||
1596 | |||
1597 | if (NULL != pc->fi->serialization) | ||
1598 | { | ||
1599 | GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO, | ||
1600 | pc->fi->serialization); | ||
1601 | GNUNET_free (pc->fi->serialization); | ||
1602 | pc->fi->serialization = NULL; | ||
1603 | } | ||
1604 | off = (NULL == pc->fi->chk_uri) ? 0 : GNUNET_ntohll ( | ||
1605 | pc->fi->chk_uri->data.chk.file_length); | ||
1606 | |||
1607 | if (NULL != pc->serialization) | ||
1608 | { | ||
1609 | GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, | ||
1610 | pc->serialization); | ||
1611 | GNUNET_free (pc->serialization); | ||
1612 | pc->serialization = NULL; | ||
1613 | } | ||
1614 | if (NULL != pc->qre) | ||
1615 | { | ||
1616 | GNUNET_DATASTORE_cancel (pc->qre); | ||
1617 | pc->qre = NULL; | ||
1618 | } | ||
1619 | pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED; | ||
1620 | GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi, off)); | ||
1621 | publish_cleanup (pc); | ||
1622 | } | ||
1623 | |||
1624 | |||
1625 | /* 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 3981ad335..000000000 --- a/src/fs/fs_publish_ksk.c +++ /dev/null | |||
@@ -1,255 +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 | |||
34 | #include "gnunet_fs_service.h" | ||
35 | #include "fs_api.h" | ||
36 | #include "fs_tree.h" | ||
37 | #include "fs_publish_ublock.h" | ||
38 | |||
39 | /** | ||
40 | * Context for the KSK publication. | ||
41 | */ | ||
42 | struct GNUNET_FS_PublishKskContext | ||
43 | { | ||
44 | /** | ||
45 | * Keywords to use. | ||
46 | */ | ||
47 | struct GNUNET_FS_Uri *ksk_uri; | ||
48 | |||
49 | /** | ||
50 | * URI to publish. | ||
51 | */ | ||
52 | struct GNUNET_FS_Uri *uri; | ||
53 | |||
54 | /** | ||
55 | * Metadata to use. | ||
56 | */ | ||
57 | struct GNUNET_FS_MetaData *meta; | ||
58 | |||
59 | /** | ||
60 | * Global FS context. | ||
61 | */ | ||
62 | struct GNUNET_FS_Handle *h; | ||
63 | |||
64 | /** | ||
65 | * UBlock publishing operation that is active. | ||
66 | */ | ||
67 | struct GNUNET_FS_PublishUblockContext *uc; | ||
68 | |||
69 | /** | ||
70 | * Handle to the datastore, NULL if we are just simulating. | ||
71 | */ | ||
72 | struct GNUNET_DATASTORE_Handle *dsh; | ||
73 | |||
74 | /** | ||
75 | * Current task. | ||
76 | */ | ||
77 | struct GNUNET_SCHEDULER_Task *ksk_task; | ||
78 | |||
79 | /** | ||
80 | * Function to call once we're done. | ||
81 | */ | ||
82 | GNUNET_FS_PublishContinuation cont; | ||
83 | |||
84 | /** | ||
85 | * Closure for cont. | ||
86 | */ | ||
87 | void *cont_cls; | ||
88 | |||
89 | /** | ||
90 | * When should the KBlocks expire? | ||
91 | */ | ||
92 | struct GNUNET_FS_BlockOptions bo; | ||
93 | |||
94 | /** | ||
95 | * Options to use. | ||
96 | */ | ||
97 | enum GNUNET_FS_PublishOptions options; | ||
98 | |||
99 | /** | ||
100 | * Keyword that we are currently processing. | ||
101 | */ | ||
102 | unsigned int i; | ||
103 | }; | ||
104 | |||
105 | |||
106 | /** | ||
107 | * Continuation of #GNUNET_FS_publish_ksk() that performs | ||
108 | * the actual publishing operation (iterating over all | ||
109 | * of the keywords). | ||
110 | * | ||
111 | * @param cls closure of type `struct PublishKskContext *` | ||
112 | */ | ||
113 | static void | ||
114 | publish_ksk_cont (void *cls); | ||
115 | |||
116 | |||
117 | /** | ||
118 | * Function called by the datastore API with | ||
119 | * the result from the PUT request. | ||
120 | * | ||
121 | * @param cls closure of type `struct GNUNET_FS_PublishKskContext *` | ||
122 | * @param msg error message (or NULL) | ||
123 | */ | ||
124 | static void | ||
125 | kb_put_cont (void *cls, | ||
126 | const char *msg) | ||
127 | { | ||
128 | struct GNUNET_FS_PublishKskContext *pkc = cls; | ||
129 | |||
130 | pkc->uc = NULL; | ||
131 | if (NULL != msg) | ||
132 | { | ||
133 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
134 | "KBlock PUT operation failed: %s\n", msg); | ||
135 | pkc->cont (pkc->cont_cls, NULL, msg); | ||
136 | GNUNET_FS_publish_ksk_cancel (pkc); | ||
137 | return; | ||
138 | } | ||
139 | pkc->ksk_task = GNUNET_SCHEDULER_add_now (&publish_ksk_cont, pkc); | ||
140 | } | ||
141 | |||
142 | |||
143 | static void | ||
144 | publish_ksk_cont (void *cls) | ||
145 | { | ||
146 | struct GNUNET_FS_PublishKskContext *pkc = cls; | ||
147 | const char *keyword; | ||
148 | |||
149 | pkc->ksk_task = NULL; | ||
150 | if ((pkc->i == pkc->ksk_uri->data.ksk.keywordCount) || | ||
151 | (NULL == pkc->dsh)) | ||
152 | { | ||
153 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
154 | "KSK PUT operation complete\n"); | ||
155 | pkc->cont (pkc->cont_cls, pkc->ksk_uri, | ||
156 | NULL); | ||
157 | GNUNET_FS_publish_ksk_cancel (pkc); | ||
158 | return; | ||
159 | } | ||
160 | keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++]; | ||
161 | pkc->uc = GNUNET_FS_publish_ublock_ (pkc->h, | ||
162 | pkc->dsh, | ||
163 | keyword + 1 /* skip '+' */, | ||
164 | NULL, | ||
165 | GNUNET_CRYPTO_ecdsa_key_get_anonymous (), | ||
166 | pkc->meta, | ||
167 | pkc->uri, | ||
168 | &pkc->bo, | ||
169 | pkc->options, | ||
170 | &kb_put_cont, pkc); | ||
171 | } | ||
172 | |||
173 | |||
174 | /** | ||
175 | * Publish a CHK under various keywords on GNUnet. | ||
176 | * | ||
177 | * @param h handle to the file sharing subsystem | ||
178 | * @param ksk_uri keywords to use | ||
179 | * @param meta metadata to use | ||
180 | * @param uri URI to refer to in the KBlock | ||
181 | * @param bo per-block options | ||
182 | * @param options publication options | ||
183 | * @param cont continuation | ||
184 | * @param cont_cls closure for cont | ||
185 | * @return NULL on error ('cont' will still be called) | ||
186 | */ | ||
187 | struct GNUNET_FS_PublishKskContext * | ||
188 | GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h, | ||
189 | const struct GNUNET_FS_Uri *ksk_uri, | ||
190 | const struct GNUNET_FS_MetaData *meta, | ||
191 | const struct GNUNET_FS_Uri *uri, | ||
192 | const struct GNUNET_FS_BlockOptions *bo, | ||
193 | enum GNUNET_FS_PublishOptions options, | ||
194 | GNUNET_FS_PublishContinuation cont, void *cont_cls) | ||
195 | { | ||
196 | struct GNUNET_FS_PublishKskContext *pkc; | ||
197 | |||
198 | GNUNET_assert (NULL != uri); | ||
199 | pkc = GNUNET_new (struct GNUNET_FS_PublishKskContext); | ||
200 | pkc->h = h; | ||
201 | pkc->bo = *bo; | ||
202 | pkc->options = options; | ||
203 | pkc->cont = cont; | ||
204 | pkc->cont_cls = cont_cls; | ||
205 | pkc->meta = GNUNET_FS_meta_data_duplicate (meta); | ||
206 | if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) | ||
207 | { | ||
208 | pkc->dsh = GNUNET_DATASTORE_connect (h->cfg); | ||
209 | if (NULL == pkc->dsh) | ||
210 | { | ||
211 | cont (cont_cls, | ||
212 | NULL, | ||
213 | _ ("Could not connect to datastore.")); | ||
214 | GNUNET_free (pkc); | ||
215 | return NULL; | ||
216 | } | ||
217 | } | ||
218 | pkc->uri = GNUNET_FS_uri_dup (uri); | ||
219 | pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri); | ||
220 | pkc->ksk_task = GNUNET_SCHEDULER_add_now (&publish_ksk_cont, pkc); | ||
221 | return pkc; | ||
222 | } | ||
223 | |||
224 | |||
225 | /** | ||
226 | * Abort the KSK publishing operation. | ||
227 | * | ||
228 | * @param pkc context of the operation to abort. | ||
229 | */ | ||
230 | void | ||
231 | GNUNET_FS_publish_ksk_cancel (struct GNUNET_FS_PublishKskContext *pkc) | ||
232 | { | ||
233 | if (NULL != pkc->ksk_task) | ||
234 | { | ||
235 | GNUNET_SCHEDULER_cancel (pkc->ksk_task); | ||
236 | pkc->ksk_task = NULL; | ||
237 | } | ||
238 | if (NULL != pkc->uc) | ||
239 | { | ||
240 | GNUNET_FS_publish_ublock_cancel_ (pkc->uc); | ||
241 | pkc->uc = NULL; | ||
242 | } | ||
243 | if (NULL != pkc->dsh) | ||
244 | { | ||
245 | GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO); | ||
246 | pkc->dsh = NULL; | ||
247 | } | ||
248 | GNUNET_FS_meta_data_destroy (pkc->meta); | ||
249 | GNUNET_FS_uri_destroy (pkc->ksk_uri); | ||
250 | GNUNET_FS_uri_destroy (pkc->uri); | ||
251 | GNUNET_free (pkc); | ||
252 | } | ||
253 | |||
254 | |||
255 | /* 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 ad12d9b08..000000000 --- a/src/fs/fs_publish_ublock.c +++ /dev/null | |||
@@ -1,299 +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 | */ | ||
45 | static void | ||
46 | derive_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 | void | ||
66 | GNUNET_FS_ublock_decrypt_ (const void *input, | ||
67 | size_t input_len, | ||
68 | const struct GNUNET_CRYPTO_EcdsaPublicKey *ns, | ||
69 | const char *label, | ||
70 | void *output) | ||
71 | { | ||
72 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
73 | struct GNUNET_CRYPTO_SymmetricSessionKey skey; | ||
74 | |||
75 | derive_ublock_encryption_key (&skey, &iv, | ||
76 | label, ns); | ||
77 | GNUNET_CRYPTO_symmetric_decrypt (input, input_len, | ||
78 | &skey, &iv, | ||
79 | output); | ||
80 | } | ||
81 | |||
82 | |||
83 | /** | ||
84 | * Context for 'ublock_put_cont'. | ||
85 | */ | ||
86 | struct GNUNET_FS_PublishUblockContext | ||
87 | { | ||
88 | /** | ||
89 | * Function to call when done. | ||
90 | */ | ||
91 | GNUNET_FS_UBlockContinuation cont; | ||
92 | |||
93 | /** | ||
94 | * Closure of 'cont'. | ||
95 | */ | ||
96 | void *cont_cls; | ||
97 | |||
98 | /** | ||
99 | * Handle for active datastore operation. | ||
100 | */ | ||
101 | struct GNUNET_DATASTORE_QueueEntry *qre; | ||
102 | |||
103 | /** | ||
104 | * Task to run continuation asynchronously. | ||
105 | */ | ||
106 | struct GNUNET_SCHEDULER_Task *task; | ||
107 | }; | ||
108 | |||
109 | |||
110 | /** | ||
111 | * Continuation of #GNUNET_FS_publish_ublock_(). | ||
112 | * | ||
113 | * @param cls closure of type "struct GNUNET_FS_PublishUblockContext*" | ||
114 | * @param success #GNUNET_SYSERR on failure (including timeout/queue drop) | ||
115 | * #GNUNET_NO if content was already there | ||
116 | * #GNUNET_YES (or other positive value) on success | ||
117 | * @param min_expiration minimum expiration time required for 0-priority content to be stored | ||
118 | * by the datacache at this time, zero for unknown, forever if we have no | ||
119 | * space for 0-priority content | ||
120 | * @param msg NULL on success, otherwise an error message | ||
121 | */ | ||
122 | static void | ||
123 | ublock_put_cont (void *cls, | ||
124 | int32_t success, | ||
125 | struct GNUNET_TIME_Absolute min_expiration, | ||
126 | const char *msg) | ||
127 | { | ||
128 | struct GNUNET_FS_PublishUblockContext *uc = cls; | ||
129 | |||
130 | uc->qre = NULL; | ||
131 | uc->cont (uc->cont_cls, msg); | ||
132 | GNUNET_free (uc); | ||
133 | } | ||
134 | |||
135 | |||
136 | /** | ||
137 | * Run the continuation. | ||
138 | * | ||
139 | * @param cls the `struct GNUNET_FS_PublishUblockContext *` | ||
140 | */ | ||
141 | static void | ||
142 | run_cont (void *cls) | ||
143 | { | ||
144 | struct GNUNET_FS_PublishUblockContext *uc = cls; | ||
145 | |||
146 | uc->task = NULL; | ||
147 | uc->cont (uc->cont_cls, NULL); | ||
148 | GNUNET_free (uc); | ||
149 | } | ||
150 | |||
151 | |||
152 | struct GNUNET_FS_PublishUblockContext * | ||
153 | GNUNET_FS_publish_ublock_ (struct GNUNET_FS_Handle *h, | ||
154 | struct GNUNET_DATASTORE_Handle *dsh, | ||
155 | const char *label, | ||
156 | const char *ulabel, | ||
157 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns, | ||
158 | const struct GNUNET_FS_MetaData *meta, | ||
159 | const struct GNUNET_FS_Uri *uri, | ||
160 | const struct GNUNET_FS_BlockOptions *bo, | ||
161 | enum GNUNET_FS_PublishOptions options, | ||
162 | GNUNET_FS_UBlockContinuation cont, void *cont_cls) | ||
163 | { | ||
164 | struct GNUNET_FS_PublishUblockContext *uc; | ||
165 | struct GNUNET_HashCode query; | ||
166 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
167 | struct GNUNET_CRYPTO_SymmetricSessionKey skey; | ||
168 | struct GNUNET_CRYPTO_EcdsaPrivateKey *nsd; | ||
169 | struct GNUNET_CRYPTO_EcdsaPublicKey pub; | ||
170 | char *uris; | ||
171 | size_t size; | ||
172 | char *kbe; | ||
173 | char *sptr; | ||
174 | ssize_t mdsize; | ||
175 | size_t slen; | ||
176 | size_t ulen; | ||
177 | struct UBlock *ub_plain; | ||
178 | struct UBlock *ub_enc; | ||
179 | |||
180 | /* compute ublock to publish */ | ||
181 | if (NULL == meta) | ||
182 | mdsize = 0; | ||
183 | else | ||
184 | mdsize = GNUNET_FS_meta_data_get_serialized_size (meta); | ||
185 | GNUNET_assert (mdsize >= 0); | ||
186 | uris = GNUNET_FS_uri_to_string (uri); | ||
187 | slen = strlen (uris) + 1; | ||
188 | if (NULL == ulabel) | ||
189 | ulen = 1; | ||
190 | else | ||
191 | ulen = strlen (ulabel) + 1; | ||
192 | size = mdsize + sizeof(struct UBlock) + slen + ulen; | ||
193 | if (size > MAX_UBLOCK_SIZE) | ||
194 | { | ||
195 | size = MAX_UBLOCK_SIZE; | ||
196 | mdsize = size - sizeof(struct UBlock) - (slen + ulen); | ||
197 | } | ||
198 | ub_plain = GNUNET_malloc (size); | ||
199 | kbe = (char *) &ub_plain[1]; | ||
200 | if (NULL != ulabel) | ||
201 | GNUNET_memcpy (kbe, ulabel, ulen); | ||
202 | kbe += ulen; | ||
203 | GNUNET_memcpy (kbe, uris, slen); | ||
204 | kbe += slen; | ||
205 | GNUNET_free (uris); | ||
206 | sptr = kbe; | ||
207 | if (NULL != meta) | ||
208 | mdsize = | ||
209 | GNUNET_FS_meta_data_serialize (meta, &sptr, mdsize, | ||
210 | GNUNET_FS_META_DATA_SERIALIZE_PART); | ||
211 | if (-1 == mdsize) | ||
212 | { | ||
213 | GNUNET_break (0); | ||
214 | GNUNET_free (ub_plain); | ||
215 | cont (cont_cls, _ ("Internal error.")); | ||
216 | return NULL; | ||
217 | } | ||
218 | size = sizeof(struct UBlock) + slen + mdsize + ulen; | ||
219 | |||
220 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
221 | "Publishing under identifier `%s'\n", | ||
222 | label); | ||
223 | /* get public key of the namespace */ | ||
224 | GNUNET_CRYPTO_ecdsa_key_get_public (ns, | ||
225 | &pub); | ||
226 | derive_ublock_encryption_key (&skey, &iv, | ||
227 | label, &pub); | ||
228 | |||
229 | /* encrypt ublock */ | ||
230 | ub_enc = GNUNET_malloc (size); | ||
231 | GNUNET_CRYPTO_symmetric_encrypt (&ub_plain[1], | ||
232 | ulen + slen + mdsize, | ||
233 | &skey, &iv, | ||
234 | &ub_enc[1]); | ||
235 | GNUNET_free (ub_plain); | ||
236 | ub_enc->purpose.size = htonl (ulen + slen + mdsize | ||
237 | + sizeof(struct UBlock) | ||
238 | - sizeof(struct GNUNET_CRYPTO_EcdsaSignature)); | ||
239 | ub_enc->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_UBLOCK); | ||
240 | |||
241 | /* derive signing-key from 'label' and public key of the namespace */ | ||
242 | nsd = GNUNET_CRYPTO_ecdsa_private_key_derive (ns, label, "fs-ublock"); | ||
243 | GNUNET_CRYPTO_ecdsa_key_get_public (nsd, | ||
244 | &ub_enc->verification_key); | ||
245 | GNUNET_assert (GNUNET_OK == | ||
246 | GNUNET_CRYPTO_ecdsa_sign_ (nsd, | ||
247 | &ub_enc->purpose, | ||
248 | &ub_enc->signature)); | ||
249 | GNUNET_CRYPTO_hash (&ub_enc->verification_key, | ||
250 | sizeof(ub_enc->verification_key), | ||
251 | &query); | ||
252 | GNUNET_free (nsd); | ||
253 | |||
254 | uc = GNUNET_new (struct GNUNET_FS_PublishUblockContext); | ||
255 | uc->cont = cont; | ||
256 | uc->cont_cls = cont_cls; | ||
257 | if (NULL != dsh) | ||
258 | { | ||
259 | uc->qre = | ||
260 | GNUNET_DATASTORE_put (dsh, | ||
261 | 0, | ||
262 | &query, | ||
263 | ulen + slen + mdsize + sizeof(struct UBlock), | ||
264 | ub_enc, | ||
265 | GNUNET_BLOCK_TYPE_FS_UBLOCK, | ||
266 | bo->content_priority, | ||
267 | bo->anonymity_level, | ||
268 | bo->replication_level, | ||
269 | bo->expiration_time, | ||
270 | -2, 1, | ||
271 | &ublock_put_cont, uc); | ||
272 | } | ||
273 | else | ||
274 | { | ||
275 | uc->task = GNUNET_SCHEDULER_add_now (&run_cont, | ||
276 | uc); | ||
277 | } | ||
278 | GNUNET_free (ub_enc); | ||
279 | return uc; | ||
280 | } | ||
281 | |||
282 | |||
283 | /** | ||
284 | * Abort UBlock publishing operation. | ||
285 | * | ||
286 | * @param uc operation to abort. | ||
287 | */ | ||
288 | void | ||
289 | GNUNET_FS_publish_ublock_cancel_ (struct GNUNET_FS_PublishUblockContext *uc) | ||
290 | { | ||
291 | if (NULL != uc->qre) | ||
292 | GNUNET_DATASTORE_cancel (uc->qre); | ||
293 | if (NULL != uc->task) | ||
294 | GNUNET_SCHEDULER_cancel (uc->task); | ||
295 | GNUNET_free (uc); | ||
296 | } | ||
297 | |||
298 | |||
299 | /* 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 4adffc6c1..000000000 --- a/src/fs/fs_publish_ublock.h +++ /dev/null | |||
@@ -1,109 +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 | |||
34 | #include "gnunet_fs_service.h" | ||
35 | #include "gnunet_identity_service.h" | ||
36 | |||
37 | |||
38 | /** | ||
39 | * Decrypt the given UBlock, storing the result in output. | ||
40 | * | ||
41 | * @param input input data | ||
42 | * @param input_len number of bytes in @a input | ||
43 | * @param ns public key under which the UBlock was stored | ||
44 | * @param label label under which the UBlock was stored | ||
45 | * @param output where to write the result, has input_len bytes | ||
46 | */ | ||
47 | void | ||
48 | GNUNET_FS_ublock_decrypt_ (const void *input, | ||
49 | size_t input_len, | ||
50 | const struct GNUNET_CRYPTO_EcdsaPublicKey *ns, | ||
51 | const char *label, | ||
52 | void *output); | ||
53 | |||
54 | |||
55 | /** | ||
56 | * Context for 'ublock_put_cont'. | ||
57 | */ | ||
58 | struct GNUNET_FS_PublishUblockContext; | ||
59 | |||
60 | |||
61 | /** | ||
62 | * Signature of a function called as the continuation of a UBlock | ||
63 | * publication. | ||
64 | * | ||
65 | * @param cls closure | ||
66 | * @param emsg error message, NULL on success | ||
67 | */ | ||
68 | typedef void (*GNUNET_FS_UBlockContinuation) (void *cls, | ||
69 | const char *emsg); | ||
70 | |||
71 | |||
72 | /** | ||
73 | * Publish a UBlock. | ||
74 | * | ||
75 | * @param h handle to the file sharing subsystem | ||
76 | * @param dsh datastore handle to use for storage operation | ||
77 | * @param label identifier to use | ||
78 | * @param ulabel update label to use, may be an empty string for none | ||
79 | * @param ns namespace to publish in | ||
80 | * @param meta metadata to use | ||
81 | * @param uri URI to refer to in the UBlock | ||
82 | * @param bo per-block options | ||
83 | * @param options publication options | ||
84 | * @param cont continuation | ||
85 | * @param cont_cls closure for @a cont | ||
86 | * @return NULL on error (@a cont will still be called) | ||
87 | */ | ||
88 | struct GNUNET_FS_PublishUblockContext * | ||
89 | GNUNET_FS_publish_ublock_ (struct GNUNET_FS_Handle *h, | ||
90 | struct GNUNET_DATASTORE_Handle *dsh, | ||
91 | const char *label, | ||
92 | const char *ulabel, | ||
93 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns, | ||
94 | const struct GNUNET_FS_MetaData *meta, | ||
95 | const struct GNUNET_FS_Uri *uri, | ||
96 | const struct GNUNET_FS_BlockOptions *bo, | ||
97 | enum GNUNET_FS_PublishOptions options, | ||
98 | GNUNET_FS_UBlockContinuation cont, void *cont_cls); | ||
99 | |||
100 | |||
101 | /** | ||
102 | * Abort UBlock publishing operation. | ||
103 | * | ||
104 | * @param uc operation to abort. | ||
105 | */ | ||
106 | void | ||
107 | GNUNET_FS_publish_ublock_cancel_ (struct GNUNET_FS_PublishUblockContext *uc); | ||
108 | |||
109 | #endif | ||
diff --git a/src/fs/fs_search.c b/src/fs/fs_search.c deleted file mode 100644 index 8b8c54c67..000000000 --- a/src/fs/fs_search.c +++ /dev/null | |||
@@ -1,1826 +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 | |||
28 | #include "gnunet_fs_service.h" | ||
29 | #include "gnunet_protocols.h" | ||
30 | #include "fs_api.h" | ||
31 | #include "fs_publish_ublock.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * Number of availability trials we perform per search result. | ||
36 | */ | ||
37 | #define AVAILABILITY_TRIALS_MAX 8 | ||
38 | |||
39 | /** | ||
40 | * Fill in all of the generic fields for a search event and | ||
41 | * call the callback. | ||
42 | * | ||
43 | * @param pi structure to fill in | ||
44 | * @param h file-sharing handle | ||
45 | * @param sc overall search context | ||
46 | * @return value returned by the callback | ||
47 | */ | ||
48 | void * | ||
49 | GNUNET_FS_search_make_status_ (struct GNUNET_FS_ProgressInfo *pi, | ||
50 | struct GNUNET_FS_Handle *h, | ||
51 | struct GNUNET_FS_SearchContext *sc) | ||
52 | { | ||
53 | void *ret; | ||
54 | |||
55 | pi->value.search.sc = sc; | ||
56 | pi->value.search.cctx = (NULL != sc) ? sc->client_info : NULL; | ||
57 | pi->value.search.pctx = | ||
58 | ((NULL == sc) || (NULL == sc->psearch_result)) | ||
59 | ? NULL | ||
60 | : sc->psearch_result->client_info; | ||
61 | pi->value.search.query = (NULL != sc) ? sc->uri : NULL; | ||
62 | pi->value.search.duration = (NULL != sc) | ||
63 | ? GNUNET_TIME_absolute_get_duration ( | ||
64 | sc->start_time) | ||
65 | : GNUNET_TIME_UNIT_ZERO; | ||
66 | pi->value.search.anonymity = (NULL != sc) ? sc->anonymity : 0; | ||
67 | pi->fsh = h; | ||
68 | ret = h->upcb (h->upcb_cls, pi); | ||
69 | return ret; | ||
70 | } | ||
71 | |||
72 | |||
73 | /** | ||
74 | * Check if the given result is identical to the given URI. | ||
75 | * | ||
76 | * @param cls points to the URI we check against | ||
77 | * @param key not used | ||
78 | * @param value a `struct GNUNET_FS_SearchResult` who's URI we | ||
79 | * should compare with | ||
80 | * @return #GNUNET_SYSERR if the result is present, | ||
81 | * #GNUNET_OK otherwise | ||
82 | */ | ||
83 | static int | ||
84 | test_result_present (void *cls, | ||
85 | const struct GNUNET_HashCode *key, | ||
86 | void *value) | ||
87 | { | ||
88 | const struct GNUNET_FS_Uri *uri = cls; | ||
89 | struct GNUNET_FS_SearchResult *sr = value; | ||
90 | |||
91 | if (GNUNET_FS_uri_test_equal (uri, sr->uri)) | ||
92 | return GNUNET_SYSERR; | ||
93 | return GNUNET_OK; | ||
94 | } | ||
95 | |||
96 | |||
97 | /** | ||
98 | * We've found a new CHK result. Let the client | ||
99 | * know about it. | ||
100 | * | ||
101 | * @param sc the search context | ||
102 | * @param sr the specific result | ||
103 | */ | ||
104 | static void | ||
105 | notify_client_chk_result (struct GNUNET_FS_SearchContext *sc, | ||
106 | struct GNUNET_FS_SearchResult *sr) | ||
107 | { | ||
108 | struct GNUNET_FS_ProgressInfo pi; | ||
109 | |||
110 | pi.status = GNUNET_FS_STATUS_SEARCH_RESULT; | ||
111 | pi.value.search.specifics.result.meta = sr->meta; | ||
112 | pi.value.search.specifics.result.uri = sr->uri; | ||
113 | pi.value.search.specifics.result.result = sr; | ||
114 | pi.value.search.specifics.result.applicability_rank = sr->optional_support; | ||
115 | sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
116 | } | ||
117 | |||
118 | |||
119 | /** | ||
120 | * We've found new information about an existing CHK result. Let the | ||
121 | * client know about it. | ||
122 | * | ||
123 | * @param sc the search context | ||
124 | * @param sr the specific result | ||
125 | */ | ||
126 | static void | ||
127 | notify_client_chk_update (struct GNUNET_FS_SearchContext *sc, | ||
128 | struct GNUNET_FS_SearchResult *sr) | ||
129 | { | ||
130 | struct GNUNET_FS_ProgressInfo pi; | ||
131 | |||
132 | pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE; | ||
133 | pi.value.search.specifics.update.cctx = sr->client_info; | ||
134 | pi.value.search.specifics.update.meta = sr->meta; | ||
135 | pi.value.search.specifics.update.uri = sr->uri; | ||
136 | pi.value.search.specifics.update.availability_rank = | ||
137 | 2 * sr->availability_success - sr->availability_trials; | ||
138 | pi.value.search.specifics.update.availability_certainty = | ||
139 | sr->availability_trials; | ||
140 | pi.value.search.specifics.update.applicability_rank = sr->optional_support; | ||
141 | pi.value.search.specifics.update.current_probe_time | ||
142 | = GNUNET_TIME_absolute_get_duration (sr->probe_active_time); | ||
143 | sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
144 | } | ||
145 | |||
146 | |||
147 | /** | ||
148 | * Context for "get_result_present". | ||
149 | */ | ||
150 | struct GetResultContext | ||
151 | { | ||
152 | /** | ||
153 | * The URI we're looking for. | ||
154 | */ | ||
155 | const struct GNUNET_FS_Uri *uri; | ||
156 | |||
157 | /** | ||
158 | * Where to store a pointer to the search | ||
159 | * result struct if we found a match. | ||
160 | */ | ||
161 | struct GNUNET_FS_SearchResult *sr; | ||
162 | }; | ||
163 | |||
164 | |||
165 | /** | ||
166 | * Check if the given result is identical to the given URI and if so | ||
167 | * return it. | ||
168 | * | ||
169 | * @param cls a `struct GetResultContext` | ||
170 | * @param key not used | ||
171 | * @param value a `struct GNUNET_FS_SearchResult` who's URI we | ||
172 | * should compare with | ||
173 | * @return #GNUNET_OK | ||
174 | */ | ||
175 | static int | ||
176 | get_result_present (void *cls, | ||
177 | const struct GNUNET_HashCode *key, | ||
178 | void *value) | ||
179 | { | ||
180 | struct GetResultContext *grc = cls; | ||
181 | struct GNUNET_FS_SearchResult *sr = value; | ||
182 | |||
183 | if (GNUNET_FS_uri_test_equal (grc->uri, sr->uri)) | ||
184 | grc->sr = sr; | ||
185 | return GNUNET_OK; | ||
186 | } | ||
187 | |||
188 | |||
189 | /** | ||
190 | * Signal result of last probe to client and then schedule next | ||
191 | * probe. | ||
192 | * | ||
193 | * @param sr search result to signal for | ||
194 | */ | ||
195 | static void | ||
196 | signal_probe_result (struct GNUNET_FS_SearchResult *sr) | ||
197 | { | ||
198 | struct GNUNET_FS_ProgressInfo pi; | ||
199 | |||
200 | pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE; | ||
201 | pi.value.search.specifics.update.cctx = sr->client_info; | ||
202 | pi.value.search.specifics.update.meta = sr->meta; | ||
203 | pi.value.search.specifics.update.uri = sr->uri; | ||
204 | pi.value.search.specifics.update.availability_rank | ||
205 | = 2 * sr->availability_success - sr->availability_trials; | ||
206 | pi.value.search.specifics.update.availability_certainty | ||
207 | = sr->availability_trials; | ||
208 | pi.value.search.specifics.update.applicability_rank = sr->optional_support; | ||
209 | pi.value.search.specifics.update.current_probe_time | ||
210 | = GNUNET_TIME_absolute_get_duration (sr->probe_active_time); | ||
211 | sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->h, sr->sc); | ||
212 | GNUNET_FS_search_start_probe_ (sr); | ||
213 | } | ||
214 | |||
215 | |||
216 | /** | ||
217 | * Handle the case where we have failed to receive a response for our probe. | ||
218 | * | ||
219 | * @param cls our `struct GNUNET_FS_SearchResult *` | ||
220 | */ | ||
221 | static void | ||
222 | probe_failure_handler (void *cls) | ||
223 | { | ||
224 | struct GNUNET_FS_SearchResult *sr = cls; | ||
225 | |||
226 | sr->probe_cancel_task = NULL; | ||
227 | sr->availability_trials++; | ||
228 | GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES); | ||
229 | sr->probe_ctx = NULL; | ||
230 | GNUNET_FS_stop_probe_ping_task_ (sr); | ||
231 | GNUNET_FS_search_result_sync_ (sr); | ||
232 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
233 | "Probe #%u for search result %p failed\n", | ||
234 | sr->availability_trials, | ||
235 | sr); | ||
236 | signal_probe_result (sr); | ||
237 | } | ||
238 | |||
239 | |||
240 | /** | ||
241 | * Handle the case where we have gotten a response for our probe. | ||
242 | * | ||
243 | * @param cls our `struct GNUNET_FS_SearchResult *` | ||
244 | */ | ||
245 | static void | ||
246 | probe_success_handler (void *cls) | ||
247 | { | ||
248 | struct GNUNET_FS_SearchResult *sr = cls; | ||
249 | |||
250 | sr->probe_cancel_task = NULL; | ||
251 | sr->availability_trials++; | ||
252 | sr->availability_success++; | ||
253 | GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES); | ||
254 | sr->probe_ctx = NULL; | ||
255 | GNUNET_FS_stop_probe_ping_task_ (sr); | ||
256 | GNUNET_FS_search_result_sync_ (sr); | ||
257 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
258 | "Probe #%u for search result %p succeeded\n", | ||
259 | sr->availability_trials, | ||
260 | sr); | ||
261 | signal_probe_result (sr); | ||
262 | } | ||
263 | |||
264 | |||
265 | /** | ||
266 | * Notification of FS that a search probe has made progress. | ||
267 | * This function is used INSTEAD of the client's event handler | ||
268 | * for downloads where the #GNUNET_FS_DOWNLOAD_IS_PROBE flag is set. | ||
269 | * | ||
270 | * @param cls closure, always NULL (!), actual closure | ||
271 | * is in the client-context of the info struct | ||
272 | * @param info details about the event, specifying the event type | ||
273 | * and various bits about the event | ||
274 | * @return client-context (for the next progress call | ||
275 | * for this operation; should be set to NULL for | ||
276 | * SUSPEND and STOPPED events). The value returned | ||
277 | * will be passed to future callbacks in the respective | ||
278 | * field in the `struct GNUNET_FS_ProgressInfo`. | ||
279 | */ | ||
280 | void * | ||
281 | GNUNET_FS_search_probe_progress_ (void *cls, | ||
282 | const struct GNUNET_FS_ProgressInfo *info) | ||
283 | { | ||
284 | struct GNUNET_FS_SearchResult *sr = info->value.download.cctx; | ||
285 | struct GNUNET_TIME_Relative dur; | ||
286 | |||
287 | switch (info->status) | ||
288 | { | ||
289 | case GNUNET_FS_STATUS_DOWNLOAD_START: | ||
290 | /* ignore */ | ||
291 | break; | ||
292 | case GNUNET_FS_STATUS_DOWNLOAD_RESUME: | ||
293 | /* probes should never be resumed */ | ||
294 | GNUNET_assert (0); | ||
295 | break; | ||
296 | case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND: | ||
297 | /* probes should never be suspended */ | ||
298 | GNUNET_break (0); | ||
299 | break; | ||
300 | case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: | ||
301 | /* ignore */ | ||
302 | break; | ||
303 | case GNUNET_FS_STATUS_DOWNLOAD_ERROR: | ||
304 | if (NULL != sr->probe_cancel_task) | ||
305 | { | ||
306 | GNUNET_SCHEDULER_cancel (sr->probe_cancel_task); | ||
307 | sr->probe_cancel_task = NULL; | ||
308 | } | ||
309 | sr->probe_cancel_task = | ||
310 | GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time, | ||
311 | &probe_failure_handler, sr); | ||
312 | break; | ||
313 | case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: | ||
314 | if (NULL != sr->probe_cancel_task) | ||
315 | { | ||
316 | GNUNET_SCHEDULER_cancel (sr->probe_cancel_task); | ||
317 | sr->probe_cancel_task = NULL; | ||
318 | } | ||
319 | sr->probe_cancel_task = | ||
320 | GNUNET_SCHEDULER_add_now (&probe_success_handler, sr); | ||
321 | break; | ||
322 | case GNUNET_FS_STATUS_DOWNLOAD_STOPPED: | ||
323 | if (NULL != sr->probe_cancel_task) | ||
324 | { | ||
325 | GNUNET_SCHEDULER_cancel (sr->probe_cancel_task); | ||
326 | sr->probe_cancel_task = NULL; | ||
327 | } | ||
328 | sr = NULL; | ||
329 | break; | ||
330 | case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: | ||
331 | if (NULL == sr->probe_cancel_task) | ||
332 | { | ||
333 | sr->probe_active_time = GNUNET_TIME_absolute_get (); | ||
334 | sr->probe_cancel_task = | ||
335 | GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time, | ||
336 | &probe_failure_handler, sr); | ||
337 | } | ||
338 | break; | ||
339 | case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: | ||
340 | if (NULL != sr->probe_cancel_task) | ||
341 | { | ||
342 | GNUNET_SCHEDULER_cancel (sr->probe_cancel_task); | ||
343 | sr->probe_cancel_task = NULL; | ||
344 | } | ||
345 | dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time); | ||
346 | sr->remaining_probe_time = | ||
347 | GNUNET_TIME_relative_subtract (sr->remaining_probe_time, dur); | ||
348 | if (0 == sr->remaining_probe_time.rel_value_us) | ||
349 | sr->probe_cancel_task = | ||
350 | GNUNET_SCHEDULER_add_now (&probe_failure_handler, sr); | ||
351 | GNUNET_FS_search_result_sync_ (sr); | ||
352 | break; | ||
353 | |||
354 | default: | ||
355 | GNUNET_break (0); | ||
356 | return NULL; | ||
357 | } | ||
358 | return sr; | ||
359 | } | ||
360 | |||
361 | |||
362 | /** | ||
363 | * Task run periodically to remind clients that a probe is active. | ||
364 | * | ||
365 | * @param cls the `struct GNUNET_FS_SearchResult` that we are probing for | ||
366 | */ | ||
367 | static void | ||
368 | probe_ping_task_cb (void *cls) | ||
369 | { | ||
370 | struct GNUNET_FS_Handle *h = cls; | ||
371 | |||
372 | for (struct GNUNET_FS_SearchResult *sr = h->probes_head; | ||
373 | NULL != sr; | ||
374 | sr = sr->next) | ||
375 | if (NULL != sr->probe_ctx->mq) | ||
376 | signal_probe_result (sr); | ||
377 | h->probe_ping_task | ||
378 | = GNUNET_SCHEDULER_add_delayed (GNUNET_FS_PROBE_UPDATE_FREQUENCY, | ||
379 | &probe_ping_task_cb, | ||
380 | h); | ||
381 | } | ||
382 | |||
383 | |||
384 | /** | ||
385 | * Start the ping task for this search result. | ||
386 | * | ||
387 | * @param sr result to start pinging for. | ||
388 | */ | ||
389 | static void | ||
390 | start_probe_ping_task (struct GNUNET_FS_SearchResult *sr) | ||
391 | { | ||
392 | struct GNUNET_FS_Handle *h = sr->h; | ||
393 | |||
394 | GNUNET_CONTAINER_DLL_insert (h->probes_head, | ||
395 | h->probes_tail, | ||
396 | sr); | ||
397 | if (NULL == h->probe_ping_task) | ||
398 | h->probe_ping_task | ||
399 | = GNUNET_SCHEDULER_add_now (&probe_ping_task_cb, | ||
400 | h); | ||
401 | } | ||
402 | |||
403 | |||
404 | /** | ||
405 | * Stop the ping task for this search result. | ||
406 | * | ||
407 | * @param sr result to start pinging for. | ||
408 | */ | ||
409 | void | ||
410 | GNUNET_FS_stop_probe_ping_task_ (struct GNUNET_FS_SearchResult *sr) | ||
411 | { | ||
412 | struct GNUNET_FS_Handle *h = sr->h; | ||
413 | |||
414 | GNUNET_CONTAINER_DLL_remove (h->probes_head, | ||
415 | h->probes_tail, | ||
416 | sr); | ||
417 | if (NULL == h->probes_head) | ||
418 | { | ||
419 | GNUNET_SCHEDULER_cancel (h->probe_ping_task); | ||
420 | h->probe_ping_task = NULL; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | |||
425 | /** | ||
426 | * Start download probes for the given search result. | ||
427 | * | ||
428 | * @param sr the search result | ||
429 | */ | ||
430 | void | ||
431 | GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr) | ||
432 | { | ||
433 | uint64_t off; | ||
434 | uint64_t len; | ||
435 | |||
436 | if (NULL != sr->probe_ctx) | ||
437 | return; | ||
438 | if (NULL != sr->download) | ||
439 | return; | ||
440 | if (0 == (sr->h->flags & GNUNET_FS_FLAGS_DO_PROBES)) | ||
441 | return; | ||
442 | if (sr->availability_trials > AVAILABILITY_TRIALS_MAX) | ||
443 | return; | ||
444 | if ( (GNUNET_FS_URI_CHK != sr->uri->type) && | ||
445 | (GNUNET_FS_URI_LOC != sr->uri->type) ) | ||
446 | return; | ||
447 | len = GNUNET_FS_uri_chk_get_file_size (sr->uri); | ||
448 | if (0 == len) | ||
449 | return; | ||
450 | if ((len <= DBLOCK_SIZE) && (sr->availability_success > 0)) | ||
451 | return; | ||
452 | off = len / DBLOCK_SIZE; | ||
453 | if (off > 0) | ||
454 | off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off); | ||
455 | off *= DBLOCK_SIZE; | ||
456 | if (len - off < DBLOCK_SIZE) | ||
457 | len = len - off; | ||
458 | else | ||
459 | len = DBLOCK_SIZE; | ||
460 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
461 | "Starting probe #%u (at offset %llu) for search result %p\n", | ||
462 | sr->availability_trials + 1, | ||
463 | (unsigned long long) off, | ||
464 | sr); | ||
465 | sr->remaining_probe_time = | ||
466 | GNUNET_TIME_relative_saturating_multiply (sr->h->avg_block_latency, | ||
467 | 2 * (1 | ||
468 | + sr->availability_trials)); | ||
469 | sr->probe_ctx = | ||
470 | GNUNET_FS_download_start (sr->h, sr->uri, sr->meta, NULL, NULL, off, | ||
471 | len, sr->anonymity, | ||
472 | GNUNET_FS_DOWNLOAD_NO_TEMPORARIES | ||
473 | | GNUNET_FS_DOWNLOAD_IS_PROBE, sr, NULL); | ||
474 | start_probe_ping_task (sr); | ||
475 | } | ||
476 | |||
477 | |||
478 | /** | ||
479 | * Start download probes for the given search result. | ||
480 | * | ||
481 | * @param h file-sharing handle to use for the operation | ||
482 | * @param uri URI to probe | ||
483 | * @param meta meta data associated with the URI | ||
484 | * @param client_info client info pointer to use for associated events | ||
485 | * @param anonymity anonymity level to use for the probes | ||
486 | * @return the search result handle to access the probe activity | ||
487 | */ | ||
488 | struct GNUNET_FS_SearchResult * | ||
489 | GNUNET_FS_probe (struct GNUNET_FS_Handle *h, | ||
490 | const struct GNUNET_FS_Uri *uri, | ||
491 | const struct GNUNET_FS_MetaData *meta, | ||
492 | void *client_info, | ||
493 | uint32_t anonymity) | ||
494 | { | ||
495 | struct GNUNET_FS_SearchResult *sr; | ||
496 | |||
497 | GNUNET_assert (NULL != h); | ||
498 | GNUNET_assert (NULL != uri); | ||
499 | sr = GNUNET_new (struct GNUNET_FS_SearchResult); | ||
500 | sr->h = h; | ||
501 | sr->uri = GNUNET_FS_uri_dup (uri); | ||
502 | sr->meta = GNUNET_FS_meta_data_duplicate (meta); | ||
503 | sr->client_info = client_info; | ||
504 | sr->anonymity = anonymity; | ||
505 | GNUNET_FS_search_start_probe_ (sr); | ||
506 | return sr; | ||
507 | } | ||
508 | |||
509 | |||
510 | /** | ||
511 | * Stop probing activity associated with a search result. | ||
512 | * | ||
513 | * @param sr search result | ||
514 | */ | ||
515 | static void | ||
516 | GNUNET_FS_search_stop_probe_ (struct GNUNET_FS_SearchResult *sr) | ||
517 | { | ||
518 | if (NULL != sr->probe_ctx) | ||
519 | { | ||
520 | GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES); | ||
521 | sr->probe_ctx = NULL; | ||
522 | GNUNET_FS_stop_probe_ping_task_ (sr); | ||
523 | } | ||
524 | if (NULL != sr->probe_cancel_task) | ||
525 | { | ||
526 | GNUNET_SCHEDULER_cancel (sr->probe_cancel_task); | ||
527 | sr->probe_cancel_task = NULL; | ||
528 | } | ||
529 | } | ||
530 | |||
531 | |||
532 | /** | ||
533 | * Stop probe activity. Must ONLY be used on values | ||
534 | * returned from #GNUNET_FS_probe. | ||
535 | * | ||
536 | * @param sr search result to stop probing for (freed) | ||
537 | * @return the value of the 'client_info' pointer | ||
538 | */ | ||
539 | void * | ||
540 | GNUNET_FS_probe_stop (struct GNUNET_FS_SearchResult *sr) | ||
541 | { | ||
542 | void *client_info; | ||
543 | |||
544 | GNUNET_assert (NULL == sr->sc); | ||
545 | GNUNET_FS_search_stop_probe_ (sr); | ||
546 | GNUNET_FS_uri_destroy (sr->uri); | ||
547 | GNUNET_FS_meta_data_destroy (sr->meta); | ||
548 | client_info = sr->client_info; | ||
549 | GNUNET_free (sr); | ||
550 | return client_info; | ||
551 | } | ||
552 | |||
553 | |||
554 | /** | ||
555 | * We have received a KSK result. Check how it fits in with the | ||
556 | * overall query and notify the client accordingly. | ||
557 | * | ||
558 | * @param sc context for the overall query | ||
559 | * @param ent entry for the specific keyword | ||
560 | * @param uri the URI that was found | ||
561 | * @param meta metadata associated with the URI | ||
562 | * under the @a ent keyword | ||
563 | */ | ||
564 | static void | ||
565 | process_ksk_result (struct GNUNET_FS_SearchContext *sc, | ||
566 | struct SearchRequestEntry *ent, | ||
567 | const struct GNUNET_FS_Uri *uri, | ||
568 | const struct GNUNET_FS_MetaData *meta) | ||
569 | { | ||
570 | struct GNUNET_HashCode key; | ||
571 | struct GNUNET_FS_SearchResult *sr; | ||
572 | struct GetResultContext grc; | ||
573 | int is_new; | ||
574 | unsigned int koff; | ||
575 | |||
576 | /* check if new */ | ||
577 | GNUNET_assert (NULL != sc); | ||
578 | if (GNUNET_OK != | ||
579 | GNUNET_FS_uri_to_key (uri, | ||
580 | &key)) | ||
581 | { | ||
582 | GNUNET_break_op (0); | ||
583 | return; | ||
584 | } | ||
585 | if (GNUNET_SYSERR == | ||
586 | GNUNET_CONTAINER_multihashmap_get_multiple (ent->results, | ||
587 | &key, | ||
588 | &test_result_present, | ||
589 | (void *) uri)) | ||
590 | return; /* duplicate result */ | ||
591 | /* try to find search result in master map */ | ||
592 | grc.sr = NULL; | ||
593 | grc.uri = uri; | ||
594 | GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map, | ||
595 | &key, | ||
596 | &get_result_present, &grc); | ||
597 | sr = grc.sr; | ||
598 | is_new = (NULL == sr) || (sr->mandatory_missing > 0); | ||
599 | if (NULL == sr) | ||
600 | { | ||
601 | sr = GNUNET_new (struct GNUNET_FS_SearchResult); | ||
602 | sr->h = sc->h; | ||
603 | sr->sc = sc; | ||
604 | sr->anonymity = sc->anonymity; | ||
605 | sr->uri = GNUNET_FS_uri_dup (uri); | ||
606 | sr->meta = GNUNET_FS_meta_data_duplicate (meta); | ||
607 | sr->mandatory_missing = sc->mandatory_count; | ||
608 | sr->key = key; | ||
609 | sr->keyword_bitmap = GNUNET_malloc ((sc->uri->data.ksk.keywordCount + 7) | ||
610 | / 8); /* round up, count bits */ | ||
611 | GNUNET_CONTAINER_multihashmap_put (sc->master_result_map, &key, sr, | ||
612 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
613 | } | ||
614 | else | ||
615 | { | ||
616 | GNUNET_FS_meta_data_merge (sr->meta, meta); | ||
617 | } | ||
618 | GNUNET_break (GNUNET_OK == | ||
619 | GNUNET_CONTAINER_multihashmap_put (ent->results, | ||
620 | &sr->key, | ||
621 | sr, | ||
622 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
623 | |||
624 | koff = ent - sc->requests; | ||
625 | GNUNET_assert ((ent >= sc->requests) && | ||
626 | (koff < sc->uri->data.ksk.keywordCount)); | ||
627 | sr->keyword_bitmap[koff / 8] |= (1 << (koff % 8)); | ||
628 | /* check if mandatory satisfied */ | ||
629 | if (1 <= GNUNET_CONTAINER_multihashmap_size (ent->results)) | ||
630 | { | ||
631 | if (ent->mandatory) | ||
632 | { | ||
633 | GNUNET_break (sr->mandatory_missing > 0); | ||
634 | sr->mandatory_missing--; | ||
635 | } | ||
636 | else | ||
637 | { | ||
638 | sr->optional_support++; | ||
639 | } | ||
640 | } | ||
641 | if (0 != sr->mandatory_missing) | ||
642 | { | ||
643 | GNUNET_break (NULL == sr->client_info); | ||
644 | return; | ||
645 | } | ||
646 | if (is_new) | ||
647 | notify_client_chk_result (sc, sr); | ||
648 | else | ||
649 | notify_client_chk_update (sc, sr); | ||
650 | GNUNET_FS_search_result_sync_ (sr); | ||
651 | GNUNET_FS_search_start_probe_ (sr); | ||
652 | } | ||
653 | |||
654 | |||
655 | /** | ||
656 | * Start search for content, internal API. | ||
657 | * | ||
658 | * @param h handle to the file sharing subsystem | ||
659 | * @param uri specifies the search parameters; can be | ||
660 | * a KSK URI or an SKS URI. | ||
661 | * @param anonymity desired level of anonymity | ||
662 | * @param options options for the search | ||
663 | * @param cctx client context | ||
664 | * @param psearch parent search result (for namespace update searches) | ||
665 | * @return context that can be used to control the search | ||
666 | */ | ||
667 | static struct GNUNET_FS_SearchContext * | ||
668 | search_start (struct GNUNET_FS_Handle *h, | ||
669 | const struct GNUNET_FS_Uri *uri, | ||
670 | uint32_t anonymity, | ||
671 | enum GNUNET_FS_SearchOptions options, | ||
672 | void *cctx, | ||
673 | struct GNUNET_FS_SearchResult *psearch); | ||
674 | |||
675 | |||
676 | /** | ||
677 | * We have received an SKS result. Start searching for updates and | ||
678 | * notify the client if it is a new result. | ||
679 | * | ||
680 | * @param sc context for the overall query | ||
681 | * @param id_update identifier for updates, NULL for none | ||
682 | * @param uri the URI that was found | ||
683 | * @param meta metadata associated with the URI | ||
684 | */ | ||
685 | static void | ||
686 | process_sks_result (struct GNUNET_FS_SearchContext *sc, | ||
687 | const char *id_update, | ||
688 | const struct GNUNET_FS_Uri *uri, | ||
689 | const struct GNUNET_FS_MetaData *meta) | ||
690 | { | ||
691 | struct GNUNET_FS_Uri uu; | ||
692 | struct GNUNET_HashCode key; | ||
693 | struct GNUNET_FS_SearchResult *sr; | ||
694 | |||
695 | /* check if new */ | ||
696 | GNUNET_assert (NULL != sc); | ||
697 | if (GNUNET_OK != | ||
698 | GNUNET_FS_uri_to_key (uri, | ||
699 | &key)) | ||
700 | { | ||
701 | GNUNET_break (0); | ||
702 | return; | ||
703 | } | ||
704 | GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key, | ||
705 | &uri->data.chk.chk.query, | ||
706 | &key); | ||
707 | if (GNUNET_SYSERR == | ||
708 | GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map, &key, | ||
709 | &test_result_present, | ||
710 | (void *) uri)) | ||
711 | return; /* duplicate result */ | ||
712 | sr = GNUNET_new (struct GNUNET_FS_SearchResult); | ||
713 | sr->h = sc->h; | ||
714 | sr->sc = sc; | ||
715 | sr->anonymity = sc->anonymity; | ||
716 | sr->uri = GNUNET_FS_uri_dup (uri); | ||
717 | sr->meta = GNUNET_FS_meta_data_duplicate (meta); | ||
718 | sr->key = key; | ||
719 | GNUNET_CONTAINER_multihashmap_put (sc->master_result_map, &key, sr, | ||
720 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
721 | GNUNET_FS_search_result_sync_ (sr); | ||
722 | GNUNET_FS_search_start_probe_ (sr); | ||
723 | /* notify client */ | ||
724 | if (0 == sr->mandatory_missing) | ||
725 | notify_client_chk_result (sc, sr); | ||
726 | else | ||
727 | GNUNET_break (NULL == sr->client_info); | ||
728 | /* search for updates */ | ||
729 | if (0 == strlen (id_update)) | ||
730 | return; /* no updates */ | ||
731 | uu.type = GNUNET_FS_URI_SKS; | ||
732 | uu.data.sks.ns = sc->uri->data.sks.ns; | ||
733 | uu.data.sks.identifier = GNUNET_strdup (id_update); | ||
734 | (void) search_start (sc->h, &uu, sc->anonymity, sc->options, NULL, sr); | ||
735 | GNUNET_free (uu.data.sks.identifier); | ||
736 | } | ||
737 | |||
738 | |||
739 | /** | ||
740 | * Decrypt a ublock using a 'keyword' as the passphrase. Given the | ||
741 | * KSK public key derived from the keyword, this function looks up | ||
742 | * the original keyword in the search context and decrypts the | ||
743 | * given ciphertext block. | ||
744 | * | ||
745 | * @param sc search context with the keywords | ||
746 | * @param dpub derived public key used for the search | ||
747 | * @param edata encrypted data | ||
748 | * @param edata_size number of bytes in @a edata (and @a data) | ||
749 | * @param data where to store the plaintext | ||
750 | * @return keyword index on success, #GNUNET_SYSERR on error (no such | ||
751 | * keyword, internal error) | ||
752 | */ | ||
753 | static int | ||
754 | decrypt_block_with_keyword (const struct GNUNET_FS_SearchContext *sc, | ||
755 | const struct GNUNET_CRYPTO_EcdsaPublicKey *dpub, | ||
756 | const void *edata, | ||
757 | size_t edata_size, | ||
758 | char *data) | ||
759 | { | ||
760 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon; | ||
761 | struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub; | ||
762 | unsigned int i; | ||
763 | |||
764 | /* find key */ | ||
765 | for (i = 0; i < sc->uri->data.ksk.keywordCount; i++) | ||
766 | if (0 == memcmp (dpub, | ||
767 | &sc->requests[i].dpub, | ||
768 | sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey))) | ||
769 | break; | ||
770 | if (i == sc->uri->data.ksk.keywordCount) | ||
771 | { | ||
772 | /* oops, does not match any of our keywords!? */ | ||
773 | GNUNET_break (0); | ||
774 | return GNUNET_SYSERR; | ||
775 | } | ||
776 | /* decrypt */ | ||
777 | anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous (); | ||
778 | GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub); | ||
779 | GNUNET_FS_ublock_decrypt_ (edata, edata_size, | ||
780 | &anon_pub, | ||
781 | sc->requests[i].keyword, | ||
782 | data); | ||
783 | return i; | ||
784 | } | ||
785 | |||
786 | |||
787 | /** | ||
788 | * Process a keyword search result. The actual type of block is | ||
789 | * a UBlock; we know it is a keyword search result because that's | ||
790 | * what we were searching for. | ||
791 | * | ||
792 | * @param sc our search context | ||
793 | * @param ub the ublock with the keyword search result | ||
794 | * @param size size of @a ub | ||
795 | */ | ||
796 | static void | ||
797 | process_kblock (struct GNUNET_FS_SearchContext *sc, | ||
798 | const struct UBlock *ub, | ||
799 | size_t size) | ||
800 | { | ||
801 | size_t j; | ||
802 | char pt[size - sizeof(struct UBlock)]; | ||
803 | const char *eos; | ||
804 | struct GNUNET_FS_MetaData *meta; | ||
805 | struct GNUNET_FS_Uri *uri; | ||
806 | char *emsg; | ||
807 | int i; | ||
808 | |||
809 | if (-1 == (i = decrypt_block_with_keyword (sc, | ||
810 | &ub->verification_key, | ||
811 | &ub[1], | ||
812 | size - sizeof(struct UBlock), | ||
813 | pt))) | ||
814 | return; | ||
815 | /* parse; pt[0] is just '\0', so we skip over that */ | ||
816 | eos = memchr (&pt[1], '\0', sizeof(pt) - 1); | ||
817 | if (NULL == eos) | ||
818 | { | ||
819 | GNUNET_break_op (0); | ||
820 | return; | ||
821 | } | ||
822 | if (NULL == (uri = GNUNET_FS_uri_parse (&pt[1], &emsg))) | ||
823 | { | ||
824 | if (GNUNET_FS_VERSION > 0x00090400) | ||
825 | { | ||
826 | /* we broke this in 0x00090300, so don't bitch | ||
827 | too loudly just one version up... */ | ||
828 | GNUNET_break_op (0); /* ublock malformed */ | ||
829 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
830 | _ ("Failed to parse URI `%s': %s\n"), | ||
831 | &pt[1], | ||
832 | emsg); | ||
833 | } | ||
834 | GNUNET_free (emsg); | ||
835 | return; | ||
836 | } | ||
837 | j = eos - pt + 1; | ||
838 | if (sizeof(pt) == j) | ||
839 | meta = GNUNET_FS_meta_data_create (); | ||
840 | else | ||
841 | meta = GNUNET_FS_meta_data_deserialize (&pt[j], sizeof(pt) - j); | ||
842 | if (NULL == meta) | ||
843 | { | ||
844 | GNUNET_break_op (0); /* ublock malformed */ | ||
845 | GNUNET_FS_uri_destroy (uri); | ||
846 | return; | ||
847 | } | ||
848 | process_ksk_result (sc, | ||
849 | &sc->requests[i], | ||
850 | uri, | ||
851 | meta); | ||
852 | |||
853 | /* clean up */ | ||
854 | GNUNET_FS_meta_data_destroy (meta); | ||
855 | GNUNET_FS_uri_destroy (uri); | ||
856 | } | ||
857 | |||
858 | |||
859 | /** | ||
860 | * Process a namespace-search result. The actual type of block is | ||
861 | * a UBlock; we know it is a namespace search result because that's | ||
862 | * what we were searching for. | ||
863 | * | ||
864 | * @param sc our search context | ||
865 | * @param ub the ublock with a namespace result | ||
866 | * @param size size of @a ub | ||
867 | */ | ||
868 | static void | ||
869 | process_sblock (struct GNUNET_FS_SearchContext *sc, | ||
870 | const struct UBlock *ub, | ||
871 | size_t size) | ||
872 | { | ||
873 | size_t len = size - sizeof(struct UBlock); | ||
874 | char pt[len]; | ||
875 | struct GNUNET_FS_Uri *uri; | ||
876 | struct GNUNET_FS_MetaData *meta; | ||
877 | const char *id; | ||
878 | const char *uris; | ||
879 | size_t off; | ||
880 | char *emsg; | ||
881 | |||
882 | GNUNET_FS_ublock_decrypt_ (&ub[1], len, | ||
883 | &sc->uri->data.sks.ns, | ||
884 | sc->uri->data.sks.identifier, | ||
885 | pt); | ||
886 | /* parse */ | ||
887 | if (0 == (off = GNUNET_STRINGS_buffer_tokenize (pt, len, 2, &id, &uris))) | ||
888 | { | ||
889 | GNUNET_break_op (0); /* ublock malformed */ | ||
890 | return; | ||
891 | } | ||
892 | if (NULL == (meta = GNUNET_FS_meta_data_deserialize (&pt[off], len | ||
893 | - off))) | ||
894 | { | ||
895 | GNUNET_break_op (0); /* ublock malformed */ | ||
896 | return; | ||
897 | } | ||
898 | if (NULL == (uri = GNUNET_FS_uri_parse (uris, &emsg))) | ||
899 | { | ||
900 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
901 | _ ("Failed to parse URI `%s': %s\n"), | ||
902 | uris, emsg); | ||
903 | GNUNET_break_op (0); /* ublock malformed */ | ||
904 | GNUNET_free (emsg); | ||
905 | GNUNET_FS_meta_data_destroy (meta); | ||
906 | return; | ||
907 | } | ||
908 | /* process */ | ||
909 | process_sks_result (sc, id, uri, meta); | ||
910 | /* clean up */ | ||
911 | GNUNET_FS_uri_destroy (uri); | ||
912 | GNUNET_FS_meta_data_destroy (meta); | ||
913 | } | ||
914 | |||
915 | |||
916 | /** | ||
917 | * Shutdown any existing connection to the FS | ||
918 | * service and try to establish a fresh one | ||
919 | * (and then re-transmit our search request). | ||
920 | * | ||
921 | * @param sc the search to reconnec | ||
922 | */ | ||
923 | static void | ||
924 | try_reconnect (struct GNUNET_FS_SearchContext *sc); | ||
925 | |||
926 | |||
927 | /** | ||
928 | * We check a result message from the service. | ||
929 | * | ||
930 | * @param cls closure | ||
931 | * @param cm result message received | ||
932 | */ | ||
933 | static int | ||
934 | check_result (void *cls, | ||
935 | const struct ClientPutMessage *cm) | ||
936 | { | ||
937 | /* payload of any variable size is OK */ | ||
938 | return GNUNET_OK; | ||
939 | } | ||
940 | |||
941 | |||
942 | /** | ||
943 | * We process a search result from the service. | ||
944 | * | ||
945 | * @param cls closure | ||
946 | * @param cm result message received | ||
947 | */ | ||
948 | static void | ||
949 | handle_result (void *cls, | ||
950 | const struct ClientPutMessage *cm) | ||
951 | { | ||
952 | struct GNUNET_FS_SearchContext *sc = cls; | ||
953 | uint16_t msize = ntohs (cm->header.size) - sizeof(*cm); | ||
954 | enum GNUNET_BLOCK_Type type = ntohl (cm->type); | ||
955 | |||
956 | if (GNUNET_TIME_absolute_get_duration (GNUNET_TIME_absolute_ntoh ( | ||
957 | cm->expiration)).rel_value_us > 0) | ||
958 | { | ||
959 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
960 | "Result received has already expired.\n"); | ||
961 | return; /* result expired */ | ||
962 | } | ||
963 | switch (type) | ||
964 | { | ||
965 | case GNUNET_BLOCK_TYPE_FS_UBLOCK: | ||
966 | if (GNUNET_FS_URI_SKS == sc->uri->type) | ||
967 | process_sblock (sc, | ||
968 | (const struct UBlock *) &cm[1], | ||
969 | msize); | ||
970 | else | ||
971 | process_kblock (sc, | ||
972 | (const struct UBlock *) &cm[1], | ||
973 | msize); | ||
974 | break; | ||
975 | |||
976 | case GNUNET_BLOCK_TYPE_ANY: | ||
977 | GNUNET_break (0); | ||
978 | break; | ||
979 | |||
980 | case GNUNET_BLOCK_TYPE_FS_DBLOCK: | ||
981 | GNUNET_break (0); | ||
982 | break; | ||
983 | |||
984 | case GNUNET_BLOCK_TYPE_FS_ONDEMAND: | ||
985 | GNUNET_break (0); | ||
986 | break; | ||
987 | |||
988 | case GNUNET_BLOCK_TYPE_FS_IBLOCK: | ||
989 | GNUNET_break (0); | ||
990 | break; | ||
991 | |||
992 | default: | ||
993 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
994 | _ ("Got result with unknown block type `%d', ignoring"), | ||
995 | type); | ||
996 | break; | ||
997 | } | ||
998 | } | ||
999 | |||
1000 | |||
1001 | /** | ||
1002 | * Schedule the transmission of the (next) search request | ||
1003 | * to the service. | ||
1004 | * | ||
1005 | * @param sc context for the search | ||
1006 | */ | ||
1007 | static void | ||
1008 | schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc); | ||
1009 | |||
1010 | |||
1011 | /** | ||
1012 | * Closure for #build_result_set(). | ||
1013 | */ | ||
1014 | struct MessageBuilderContext | ||
1015 | { | ||
1016 | /** | ||
1017 | * How many entries can we store to xoff. | ||
1018 | */ | ||
1019 | unsigned int put_cnt; | ||
1020 | |||
1021 | /** | ||
1022 | * How many entries should we skip. | ||
1023 | */ | ||
1024 | unsigned int skip_cnt; | ||
1025 | |||
1026 | /** | ||
1027 | * Where to store the keys. | ||
1028 | */ | ||
1029 | struct GNUNET_HashCode *xoff; | ||
1030 | |||
1031 | /** | ||
1032 | * Search context we are iterating for. | ||
1033 | */ | ||
1034 | struct GNUNET_FS_SearchContext *sc; | ||
1035 | |||
1036 | /** | ||
1037 | * Keyword offset the search result must match (0 for SKS) | ||
1038 | */ | ||
1039 | unsigned int keyword_offset; | ||
1040 | }; | ||
1041 | |||
1042 | |||
1043 | /** | ||
1044 | * Iterating over the known results, pick those matching the given | ||
1045 | * result range and store their keys at 'xoff'. | ||
1046 | * | ||
1047 | * @param cls the `struct MessageBuilderContext` | ||
1048 | * @param key key for a result | ||
1049 | * @param value the search result | ||
1050 | * @return #GNUNET_OK to continue iterating | ||
1051 | */ | ||
1052 | static int | ||
1053 | build_result_set (void *cls, | ||
1054 | const struct GNUNET_HashCode *key, | ||
1055 | void *value) | ||
1056 | { | ||
1057 | struct MessageBuilderContext *mbc = cls; | ||
1058 | struct GNUNET_FS_SearchResult *sr = value; | ||
1059 | |||
1060 | if ((NULL != sr->keyword_bitmap) && | ||
1061 | (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1 | ||
1062 | << (mbc-> | ||
1063 | keyword_offset | ||
1064 | % 8))))) | ||
1065 | return GNUNET_OK; /* have no match for this keyword yet */ | ||
1066 | if (mbc->skip_cnt > 0) | ||
1067 | { | ||
1068 | mbc->skip_cnt--; | ||
1069 | return GNUNET_OK; | ||
1070 | } | ||
1071 | if (0 == mbc->put_cnt) | ||
1072 | return GNUNET_SYSERR; | ||
1073 | mbc->xoff[--mbc->put_cnt] = *key; | ||
1074 | |||
1075 | return GNUNET_OK; | ||
1076 | } | ||
1077 | |||
1078 | |||
1079 | /** | ||
1080 | * Iterating over the known results, count those matching the given | ||
1081 | * result range and increment put count for each. | ||
1082 | * | ||
1083 | * @param cls the `struct MessageBuilderContext` | ||
1084 | * @param key key for a result | ||
1085 | * @param value the search result | ||
1086 | * @return #GNUNET_OK to continue iterating | ||
1087 | */ | ||
1088 | static int | ||
1089 | find_result_set (void *cls, | ||
1090 | const struct GNUNET_HashCode *key, | ||
1091 | void *value) | ||
1092 | { | ||
1093 | struct MessageBuilderContext *mbc = cls; | ||
1094 | struct GNUNET_FS_SearchResult *sr = value; | ||
1095 | |||
1096 | if ((NULL != sr->keyword_bitmap) && | ||
1097 | (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1 | ||
1098 | << (mbc-> | ||
1099 | keyword_offset | ||
1100 | % 8))))) | ||
1101 | return GNUNET_OK; /* have no match for this keyword yet */ | ||
1102 | mbc->put_cnt++; | ||
1103 | return GNUNET_OK; | ||
1104 | } | ||
1105 | |||
1106 | |||
1107 | /** | ||
1108 | * Schedule the transmission of the (next) search request | ||
1109 | * to the service. | ||
1110 | * | ||
1111 | * @param sc context for the search | ||
1112 | */ | ||
1113 | static void | ||
1114 | schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc) | ||
1115 | { | ||
1116 | struct MessageBuilderContext mbc; | ||
1117 | struct GNUNET_MQ_Envelope *env; | ||
1118 | struct SearchMessage *sm; | ||
1119 | struct GNUNET_CRYPTO_EcdsaPublicKey dpub; | ||
1120 | unsigned int total_seen_results; /* total number of result hashes to send */ | ||
1121 | uint32_t options; | ||
1122 | unsigned int left; | ||
1123 | unsigned int todo; | ||
1124 | unsigned int fit; | ||
1125 | unsigned int search_request_map_offset; | ||
1126 | unsigned int keyword_offset; | ||
1127 | int first_call; | ||
1128 | |||
1129 | memset (&mbc, 0, sizeof(mbc)); | ||
1130 | mbc.sc = sc; | ||
1131 | if (GNUNET_FS_uri_test_ksk (sc->uri)) | ||
1132 | { | ||
1133 | /* This will calculate the result set size ONLY for | ||
1134 | "keyword_offset == 0", so we will have to recalculate | ||
1135 | it for the other keywords later! */ | ||
1136 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1137 | &find_result_set, | ||
1138 | &mbc); | ||
1139 | total_seen_results = mbc.put_cnt; | ||
1140 | } | ||
1141 | else | ||
1142 | { | ||
1143 | total_seen_results | ||
1144 | = GNUNET_CONTAINER_multihashmap_size (sc->master_result_map); | ||
1145 | } | ||
1146 | search_request_map_offset = 0; | ||
1147 | keyword_offset = 0; | ||
1148 | first_call = GNUNET_YES; | ||
1149 | while ((0 != (left = | ||
1150 | (total_seen_results - search_request_map_offset))) || | ||
1151 | (GNUNET_YES == first_call)) | ||
1152 | { | ||
1153 | first_call = GNUNET_NO; | ||
1154 | options = SEARCH_MESSAGE_OPTION_NONE; | ||
1155 | if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY)) | ||
1156 | options |= SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY; | ||
1157 | |||
1158 | fit = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(*sm)) / sizeof(struct | ||
1159 | GNUNET_HashCode); | ||
1160 | todo = GNUNET_MIN (fit, | ||
1161 | left); | ||
1162 | env = GNUNET_MQ_msg_extra (sm, | ||
1163 | sizeof(struct GNUNET_HashCode) * todo, | ||
1164 | GNUNET_MESSAGE_TYPE_FS_START_SEARCH); | ||
1165 | mbc.skip_cnt = search_request_map_offset; | ||
1166 | mbc.xoff = (struct GNUNET_HashCode *) &sm[1]; | ||
1167 | sm->type = htonl (GNUNET_BLOCK_TYPE_FS_UBLOCK); | ||
1168 | sm->anonymity_level = htonl (sc->anonymity); | ||
1169 | memset (&sm->target, | ||
1170 | 0, | ||
1171 | sizeof(struct GNUNET_PeerIdentity)); | ||
1172 | |||
1173 | if (GNUNET_FS_uri_test_ksk (sc->uri)) | ||
1174 | { | ||
1175 | mbc.keyword_offset = keyword_offset; | ||
1176 | /* calculate how many results we can send in this message */ | ||
1177 | mbc.put_cnt = todo; | ||
1178 | /* now build message */ | ||
1179 | sm->query = sc->requests[keyword_offset].uquery; | ||
1180 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1181 | &build_result_set, | ||
1182 | &mbc); | ||
1183 | search_request_map_offset += todo; | ||
1184 | GNUNET_assert (0 == mbc.put_cnt); | ||
1185 | GNUNET_assert (total_seen_results >= search_request_map_offset); | ||
1186 | if (total_seen_results != search_request_map_offset) | ||
1187 | { | ||
1188 | /* more requesting to be done... */ | ||
1189 | sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED); | ||
1190 | } | ||
1191 | else | ||
1192 | { | ||
1193 | sm->options = htonl (options); | ||
1194 | keyword_offset++; | ||
1195 | if (sc->uri->data.ksk.keywordCount != keyword_offset) | ||
1196 | { | ||
1197 | /* more keywords => more requesting to be done... */ | ||
1198 | first_call = GNUNET_YES; | ||
1199 | search_request_map_offset = 0; | ||
1200 | mbc.put_cnt = 0; | ||
1201 | mbc.keyword_offset = keyword_offset; | ||
1202 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1203 | &find_result_set, | ||
1204 | &mbc); | ||
1205 | total_seen_results = mbc.put_cnt; | ||
1206 | } | ||
1207 | } | ||
1208 | } | ||
1209 | else | ||
1210 | { | ||
1211 | GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri)); | ||
1212 | |||
1213 | GNUNET_CRYPTO_ecdsa_public_key_derive (&sc->uri->data.sks.ns, | ||
1214 | sc->uri->data.sks.identifier, | ||
1215 | "fs-ublock", | ||
1216 | &dpub); | ||
1217 | GNUNET_CRYPTO_hash (&dpub, | ||
1218 | sizeof(dpub), | ||
1219 | &sm->query); | ||
1220 | mbc.put_cnt = todo; | ||
1221 | mbc.keyword_offset = 0; | ||
1222 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1223 | &build_result_set, | ||
1224 | &mbc); | ||
1225 | GNUNET_assert (total_seen_results >= search_request_map_offset); | ||
1226 | if (total_seen_results != search_request_map_offset) | ||
1227 | { | ||
1228 | /* more requesting to be done... */ | ||
1229 | sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED); | ||
1230 | } | ||
1231 | else | ||
1232 | { | ||
1233 | sm->options = htonl (options); | ||
1234 | } | ||
1235 | } | ||
1236 | GNUNET_MQ_send (sc->mq, | ||
1237 | env); | ||
1238 | } | ||
1239 | } | ||
1240 | |||
1241 | |||
1242 | /** | ||
1243 | * Generic error handler, called with the appropriate error code and | ||
1244 | * the same closure specified at the creation of the message queue. | ||
1245 | * Not every message queue implementation supports an error handler. | ||
1246 | * | ||
1247 | * @param cls closure with the `struct GNUNET_FS_SearchContext *` | ||
1248 | * @param error error code | ||
1249 | */ | ||
1250 | static void | ||
1251 | search_mq_error_handler (void *cls, | ||
1252 | enum GNUNET_MQ_Error error) | ||
1253 | { | ||
1254 | struct GNUNET_FS_SearchContext *sc = cls; | ||
1255 | |||
1256 | if (NULL != sc->mq) | ||
1257 | { | ||
1258 | GNUNET_MQ_destroy (sc->mq); | ||
1259 | sc->mq = NULL; | ||
1260 | } | ||
1261 | try_reconnect (sc); | ||
1262 | } | ||
1263 | |||
1264 | |||
1265 | /** | ||
1266 | * Reconnect to the FS service and transmit | ||
1267 | * our queries NOW. | ||
1268 | * | ||
1269 | * @param cls our search context | ||
1270 | */ | ||
1271 | static void | ||
1272 | do_reconnect (void *cls) | ||
1273 | { | ||
1274 | struct GNUNET_FS_SearchContext *sc = cls; | ||
1275 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
1276 | GNUNET_MQ_hd_var_size (result, | ||
1277 | GNUNET_MESSAGE_TYPE_FS_PUT, | ||
1278 | struct ClientPutMessage, | ||
1279 | sc), | ||
1280 | GNUNET_MQ_handler_end () | ||
1281 | }; | ||
1282 | |||
1283 | sc->task = NULL; | ||
1284 | sc->mq = GNUNET_CLIENT_connect (sc->h->cfg, | ||
1285 | "fs", | ||
1286 | handlers, | ||
1287 | &search_mq_error_handler, | ||
1288 | sc); | ||
1289 | if (NULL == sc->mq) | ||
1290 | { | ||
1291 | try_reconnect (sc); | ||
1292 | return; | ||
1293 | } | ||
1294 | schedule_transmit_search_request (sc); | ||
1295 | } | ||
1296 | |||
1297 | |||
1298 | /** | ||
1299 | * Shutdown any existing connection to the FS | ||
1300 | * service and try to establish a fresh one | ||
1301 | * (and then re-transmit our search request). | ||
1302 | * | ||
1303 | * @param sc the search to reconnec | ||
1304 | */ | ||
1305 | static void | ||
1306 | try_reconnect (struct GNUNET_FS_SearchContext *sc) | ||
1307 | { | ||
1308 | if (NULL != sc->mq) | ||
1309 | { | ||
1310 | GNUNET_MQ_destroy (sc->mq); | ||
1311 | sc->mq = NULL; | ||
1312 | } | ||
1313 | sc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (sc->reconnect_backoff); | ||
1314 | sc->task = | ||
1315 | GNUNET_SCHEDULER_add_delayed (sc->reconnect_backoff, | ||
1316 | &do_reconnect, | ||
1317 | sc); | ||
1318 | } | ||
1319 | |||
1320 | |||
1321 | /** | ||
1322 | * Start search for content, internal API. | ||
1323 | * | ||
1324 | * @param h handle to the file sharing subsystem | ||
1325 | * @param uri specifies the search parameters; can be | ||
1326 | * a KSK URI or an SKS URI. | ||
1327 | * @param anonymity desired level of anonymity | ||
1328 | * @param options options for the search | ||
1329 | * @param cctx initial value for the client context | ||
1330 | * @param psearch parent search result (for namespace update searches) | ||
1331 | * @return context that can be used to control the search | ||
1332 | */ | ||
1333 | static struct GNUNET_FS_SearchContext * | ||
1334 | search_start (struct GNUNET_FS_Handle *h, | ||
1335 | const struct GNUNET_FS_Uri *uri, | ||
1336 | uint32_t anonymity, | ||
1337 | enum GNUNET_FS_SearchOptions options, | ||
1338 | void *cctx, | ||
1339 | struct GNUNET_FS_SearchResult *psearch) | ||
1340 | { | ||
1341 | struct GNUNET_FS_SearchContext *sc; | ||
1342 | struct GNUNET_FS_ProgressInfo pi; | ||
1343 | |||
1344 | sc = GNUNET_new (struct GNUNET_FS_SearchContext); | ||
1345 | sc->h = h; | ||
1346 | sc->options = options; | ||
1347 | sc->uri = GNUNET_FS_uri_dup (uri); | ||
1348 | sc->anonymity = anonymity; | ||
1349 | sc->start_time = GNUNET_TIME_absolute_get (); | ||
1350 | if (NULL != psearch) | ||
1351 | { | ||
1352 | sc->psearch_result = psearch; | ||
1353 | psearch->update_search = sc; | ||
1354 | } | ||
1355 | sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO); | ||
1356 | sc->client_info = cctx; | ||
1357 | if (GNUNET_OK != GNUNET_FS_search_start_searching_ (sc)) | ||
1358 | { | ||
1359 | GNUNET_FS_uri_destroy (sc->uri); | ||
1360 | GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map); | ||
1361 | GNUNET_free (sc); | ||
1362 | return NULL; | ||
1363 | } | ||
1364 | GNUNET_FS_search_sync_ (sc); | ||
1365 | pi.status = GNUNET_FS_STATUS_SEARCH_START; | ||
1366 | sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
1367 | return sc; | ||
1368 | } | ||
1369 | |||
1370 | |||
1371 | /** | ||
1372 | * Update the 'results' map for the individual keywords with the | ||
1373 | * results from the 'global' result set. | ||
1374 | * | ||
1375 | * @param cls closure, the `struct GNUNET_FS_SearchContext *` | ||
1376 | * @param key current key code | ||
1377 | * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *` | ||
1378 | * @return #GNUNET_YES (we should continue to iterate) | ||
1379 | */ | ||
1380 | static int | ||
1381 | update_sre_result_maps (void *cls, | ||
1382 | const struct GNUNET_HashCode *key, | ||
1383 | void *value) | ||
1384 | { | ||
1385 | struct GNUNET_FS_SearchContext *sc = cls; | ||
1386 | struct GNUNET_FS_SearchResult *sr = value; | ||
1387 | |||
1388 | for (unsigned int i = 0; i < sc->uri->data.ksk.keywordCount; i++) | ||
1389 | { | ||
1390 | if (0 != (sr->keyword_bitmap[i / 8] & (1 << (i % 8)))) | ||
1391 | GNUNET_break (GNUNET_OK == | ||
1392 | GNUNET_CONTAINER_multihashmap_put (sc->requests[i].results, | ||
1393 | &sr->key, | ||
1394 | sr, | ||
1395 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
1396 | } | ||
1397 | return GNUNET_YES; | ||
1398 | } | ||
1399 | |||
1400 | |||
1401 | int | ||
1402 | GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc) | ||
1403 | { | ||
1404 | unsigned int i; | ||
1405 | const char *keyword; | ||
1406 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon; | ||
1407 | struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub; | ||
1408 | struct SearchRequestEntry *sre; | ||
1409 | |||
1410 | GNUNET_assert (NULL == sc->mq); | ||
1411 | if (GNUNET_FS_uri_test_ksk (sc->uri)) | ||
1412 | { | ||
1413 | GNUNET_assert (0 != sc->uri->data.ksk.keywordCount); | ||
1414 | anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous (); | ||
1415 | GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub); | ||
1416 | sc->requests | ||
1417 | = GNUNET_new_array (sc->uri->data.ksk.keywordCount, | ||
1418 | struct SearchRequestEntry); | ||
1419 | |||
1420 | for (i = 0; i < sc->uri->data.ksk.keywordCount; i++) | ||
1421 | { | ||
1422 | keyword = &sc->uri->data.ksk.keywords[i][1]; | ||
1423 | sre = &sc->requests[i]; | ||
1424 | sre->keyword = GNUNET_strdup (keyword); | ||
1425 | GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub, | ||
1426 | keyword, | ||
1427 | "fs-ublock", | ||
1428 | &sre->dpub); | ||
1429 | GNUNET_CRYPTO_hash (&sre->dpub, | ||
1430 | sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey), | ||
1431 | &sre->uquery); | ||
1432 | sre->mandatory = (sc->uri->data.ksk.keywords[i][0] == '+'); | ||
1433 | if (sre->mandatory) | ||
1434 | sc->mandatory_count++; | ||
1435 | sre->results = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO); | ||
1436 | } | ||
1437 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1438 | &update_sre_result_maps, | ||
1439 | sc); | ||
1440 | } | ||
1441 | GNUNET_assert (NULL == sc->task); | ||
1442 | do_reconnect (sc); | ||
1443 | if (NULL == sc->mq) | ||
1444 | { | ||
1445 | GNUNET_SCHEDULER_cancel (sc->task); | ||
1446 | sc->task = NULL; | ||
1447 | return GNUNET_SYSERR; | ||
1448 | } | ||
1449 | return GNUNET_OK; | ||
1450 | } | ||
1451 | |||
1452 | |||
1453 | /** | ||
1454 | * Freeze probes for the given search result. | ||
1455 | * | ||
1456 | * @param cls the global FS handle | ||
1457 | * @param key the key for the search result (unused) | ||
1458 | * @param value the search result to free | ||
1459 | * @return #GNUNET_OK | ||
1460 | */ | ||
1461 | static int | ||
1462 | search_result_freeze_probes (void *cls, | ||
1463 | const struct GNUNET_HashCode *key, | ||
1464 | void *value) | ||
1465 | { | ||
1466 | struct GNUNET_FS_SearchResult *sr = value; | ||
1467 | |||
1468 | if (NULL != sr->probe_ctx) | ||
1469 | { | ||
1470 | GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES); | ||
1471 | sr->probe_ctx = NULL; | ||
1472 | GNUNET_FS_stop_probe_ping_task_ (sr); | ||
1473 | } | ||
1474 | if (NULL != sr->probe_cancel_task) | ||
1475 | { | ||
1476 | GNUNET_SCHEDULER_cancel (sr->probe_cancel_task); | ||
1477 | sr->probe_cancel_task = NULL; | ||
1478 | } | ||
1479 | if (NULL != sr->update_search) | ||
1480 | GNUNET_FS_search_pause (sr->update_search); | ||
1481 | return GNUNET_OK; | ||
1482 | } | ||
1483 | |||
1484 | |||
1485 | /** | ||
1486 | * Resume probes for the given search result. | ||
1487 | * | ||
1488 | * @param cls the global FS handle | ||
1489 | * @param key the key for the search result (unused) | ||
1490 | * @param value the search result to free | ||
1491 | * @return #GNUNET_OK | ||
1492 | */ | ||
1493 | static int | ||
1494 | search_result_resume_probes (void *cls, | ||
1495 | const struct GNUNET_HashCode *key, | ||
1496 | void *value) | ||
1497 | { | ||
1498 | struct GNUNET_FS_SearchResult *sr = value; | ||
1499 | |||
1500 | GNUNET_FS_search_start_probe_ (sr); | ||
1501 | if (NULL != sr->update_search) | ||
1502 | GNUNET_FS_search_continue (sr->update_search); | ||
1503 | return GNUNET_OK; | ||
1504 | } | ||
1505 | |||
1506 | |||
1507 | /** | ||
1508 | * Signal suspend and free the given search result. | ||
1509 | * | ||
1510 | * @param cls the global FS handle | ||
1511 | * @param key the key for the search result (unused) | ||
1512 | * @param value the search result to free | ||
1513 | * @return #GNUNET_OK | ||
1514 | */ | ||
1515 | static int | ||
1516 | search_result_suspend (void *cls, | ||
1517 | const struct GNUNET_HashCode *key, | ||
1518 | void *value) | ||
1519 | { | ||
1520 | struct GNUNET_FS_SearchContext *sc = cls; | ||
1521 | struct GNUNET_FS_SearchResult *sr = value; | ||
1522 | struct GNUNET_FS_ProgressInfo pi; | ||
1523 | |||
1524 | if (NULL != sr->download) | ||
1525 | { | ||
1526 | GNUNET_FS_download_signal_suspend_ (sr->download); | ||
1527 | sr->download = NULL; | ||
1528 | } | ||
1529 | if (NULL != sr->update_search) | ||
1530 | { | ||
1531 | GNUNET_FS_search_signal_suspend_ (sr->update_search); | ||
1532 | sr->update_search = NULL; | ||
1533 | } | ||
1534 | GNUNET_FS_search_stop_probe_ (sr); | ||
1535 | if (0 == sr->mandatory_missing) | ||
1536 | { | ||
1537 | /* client is aware of search result, notify about suspension event */ | ||
1538 | pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND; | ||
1539 | pi.value.search.specifics.result_suspend.cctx = sr->client_info; | ||
1540 | pi.value.search.specifics.result_suspend.meta = sr->meta; | ||
1541 | pi.value.search.specifics.result_suspend.uri = sr->uri; | ||
1542 | sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
1543 | } | ||
1544 | GNUNET_break (NULL == sr->client_info); | ||
1545 | GNUNET_free (sr->serialization); | ||
1546 | GNUNET_FS_uri_destroy (sr->uri); | ||
1547 | GNUNET_FS_meta_data_destroy (sr->meta); | ||
1548 | GNUNET_free (sr->keyword_bitmap); | ||
1549 | GNUNET_free (sr); | ||
1550 | return GNUNET_OK; | ||
1551 | } | ||
1552 | |||
1553 | |||
1554 | void | ||
1555 | GNUNET_FS_search_signal_suspend_ (void *cls) | ||
1556 | { | ||
1557 | struct GNUNET_FS_SearchContext *sc = cls; | ||
1558 | struct GNUNET_FS_ProgressInfo pi; | ||
1559 | unsigned int i; | ||
1560 | |||
1561 | GNUNET_FS_end_top (sc->h, sc->top); | ||
1562 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1563 | &search_result_suspend, sc); | ||
1564 | pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND; | ||
1565 | sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
1566 | GNUNET_break (NULL == sc->client_info); | ||
1567 | if (sc->task != NULL) | ||
1568 | { | ||
1569 | GNUNET_SCHEDULER_cancel (sc->task); | ||
1570 | sc->task = NULL; | ||
1571 | } | ||
1572 | if (NULL != sc->mq) | ||
1573 | { | ||
1574 | GNUNET_MQ_destroy (sc->mq); | ||
1575 | sc->mq = NULL; | ||
1576 | } | ||
1577 | GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map); | ||
1578 | if (NULL != sc->requests) | ||
1579 | { | ||
1580 | GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri)); | ||
1581 | for (i = 0; i < sc->uri->data.ksk.keywordCount; i++) | ||
1582 | { | ||
1583 | GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results); | ||
1584 | GNUNET_free (sc->requests[i].keyword); | ||
1585 | } | ||
1586 | } | ||
1587 | GNUNET_free (sc->requests); | ||
1588 | GNUNET_free (sc->emsg); | ||
1589 | GNUNET_FS_uri_destroy (sc->uri); | ||
1590 | GNUNET_free (sc->serialization); | ||
1591 | GNUNET_free (sc); | ||
1592 | } | ||
1593 | |||
1594 | |||
1595 | /** | ||
1596 | * Start search for content. | ||
1597 | * | ||
1598 | * @param h handle to the file sharing subsystem | ||
1599 | * @param uri specifies the search parameters; can be | ||
1600 | * a KSK URI or an SKS URI. | ||
1601 | * @param anonymity desired level of anonymity | ||
1602 | * @param options options for the search | ||
1603 | * @param cctx initial value for the client context | ||
1604 | * @return context that can be used to control the search | ||
1605 | */ | ||
1606 | struct GNUNET_FS_SearchContext * | ||
1607 | GNUNET_FS_search_start (struct GNUNET_FS_Handle *h, | ||
1608 | const struct GNUNET_FS_Uri *uri, uint32_t anonymity, | ||
1609 | enum GNUNET_FS_SearchOptions options, void *cctx) | ||
1610 | { | ||
1611 | struct GNUNET_FS_SearchContext *ret; | ||
1612 | |||
1613 | ret = search_start (h, uri, anonymity, options, cctx, NULL); | ||
1614 | if (NULL == ret) | ||
1615 | return NULL; | ||
1616 | ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, ret); | ||
1617 | return ret; | ||
1618 | } | ||
1619 | |||
1620 | |||
1621 | /** | ||
1622 | * Pause search. | ||
1623 | * | ||
1624 | * @param sc context for the search that should be paused | ||
1625 | */ | ||
1626 | void | ||
1627 | GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc) | ||
1628 | { | ||
1629 | struct GNUNET_FS_ProgressInfo pi; | ||
1630 | |||
1631 | if (NULL != sc->task) | ||
1632 | { | ||
1633 | GNUNET_SCHEDULER_cancel (sc->task); | ||
1634 | sc->task = NULL; | ||
1635 | } | ||
1636 | if (NULL != sc->mq) | ||
1637 | { | ||
1638 | GNUNET_MQ_destroy (sc->mq); | ||
1639 | sc->mq = NULL; | ||
1640 | } | ||
1641 | GNUNET_FS_search_sync_ (sc); | ||
1642 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1643 | &search_result_freeze_probes, | ||
1644 | sc); | ||
1645 | pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED; | ||
1646 | sc->client_info = GNUNET_FS_search_make_status_ (&pi, | ||
1647 | sc->h, | ||
1648 | sc); | ||
1649 | } | ||
1650 | |||
1651 | |||
1652 | /** | ||
1653 | * Continue paused search. | ||
1654 | * | ||
1655 | * @param sc context for the search that should be resumed | ||
1656 | */ | ||
1657 | void | ||
1658 | GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc) | ||
1659 | { | ||
1660 | struct GNUNET_FS_ProgressInfo pi; | ||
1661 | |||
1662 | GNUNET_assert (NULL == sc->mq); | ||
1663 | GNUNET_assert (NULL == sc->task); | ||
1664 | do_reconnect (sc); | ||
1665 | GNUNET_FS_search_sync_ (sc); | ||
1666 | pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED; | ||
1667 | sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
1668 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1669 | &search_result_resume_probes, sc); | ||
1670 | } | ||
1671 | |||
1672 | |||
1673 | /** | ||
1674 | * Signal stop for the given search result. | ||
1675 | * | ||
1676 | * @param cls the global FS handle | ||
1677 | * @param key the key for the search result (unused) | ||
1678 | * @param value the search result to free | ||
1679 | * @return #GNUNET_OK | ||
1680 | */ | ||
1681 | static int | ||
1682 | search_result_stop (void *cls, | ||
1683 | const struct GNUNET_HashCode *key, | ||
1684 | void *value) | ||
1685 | { | ||
1686 | struct GNUNET_FS_SearchContext *sc = cls; | ||
1687 | struct GNUNET_FS_SearchResult *sr = value; | ||
1688 | struct GNUNET_FS_ProgressInfo pi; | ||
1689 | |||
1690 | GNUNET_FS_search_stop_probe_ (sr); | ||
1691 | if (NULL != sr->download) | ||
1692 | { | ||
1693 | sr->download->search = NULL; | ||
1694 | sr->download->top | ||
1695 | = GNUNET_FS_make_top (sr->download->h, | ||
1696 | &GNUNET_FS_download_signal_suspend_, | ||
1697 | sr->download); | ||
1698 | if (NULL != sr->download->serialization) | ||
1699 | { | ||
1700 | GNUNET_FS_remove_sync_file_ (sc->h, | ||
1701 | GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD, | ||
1702 | sr->download->serialization); | ||
1703 | GNUNET_free (sr->download->serialization); | ||
1704 | sr->download->serialization = NULL; | ||
1705 | } | ||
1706 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT; | ||
1707 | GNUNET_FS_download_make_status_ (&pi, | ||
1708 | sr->download); | ||
1709 | GNUNET_FS_download_sync_ (sr->download); | ||
1710 | sr->download = NULL; | ||
1711 | } | ||
1712 | if (0 != sr->mandatory_missing) | ||
1713 | { | ||
1714 | /* client is unaware of search result as | ||
1715 | it does not match required keywords */ | ||
1716 | GNUNET_break (NULL == sr->client_info); | ||
1717 | return GNUNET_OK; | ||
1718 | } | ||
1719 | pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED; | ||
1720 | pi.value.search.specifics.result_stopped.cctx = sr->client_info; | ||
1721 | pi.value.search.specifics.result_stopped.meta = sr->meta; | ||
1722 | pi.value.search.specifics.result_stopped.uri = sr->uri; | ||
1723 | sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->h, sc); | ||
1724 | return GNUNET_OK; | ||
1725 | } | ||
1726 | |||
1727 | |||
1728 | /** | ||
1729 | * Free the given search result. | ||
1730 | * | ||
1731 | * @param cls the global FS handle | ||
1732 | * @param key the key for the search result (unused) | ||
1733 | * @param value the search result to free | ||
1734 | * @return #GNUNET_OK | ||
1735 | */ | ||
1736 | static int | ||
1737 | search_result_free (void *cls, | ||
1738 | const struct GNUNET_HashCode *key, | ||
1739 | void *value) | ||
1740 | { | ||
1741 | struct GNUNET_FS_SearchResult *sr = value; | ||
1742 | |||
1743 | if (NULL != sr->update_search) | ||
1744 | { | ||
1745 | GNUNET_FS_search_stop (sr->update_search); | ||
1746 | GNUNET_assert (NULL == sr->update_search); | ||
1747 | } | ||
1748 | GNUNET_break (NULL == sr->probe_ctx); | ||
1749 | GNUNET_break (NULL == sr->probe_cancel_task); | ||
1750 | GNUNET_break (NULL == sr->client_info); | ||
1751 | GNUNET_free (sr->serialization); | ||
1752 | GNUNET_FS_uri_destroy (sr->uri); | ||
1753 | GNUNET_FS_meta_data_destroy (sr->meta); | ||
1754 | GNUNET_free (sr->keyword_bitmap); | ||
1755 | GNUNET_free (sr); | ||
1756 | return GNUNET_OK; | ||
1757 | } | ||
1758 | |||
1759 | |||
1760 | /** | ||
1761 | * Stop search for content. | ||
1762 | * | ||
1763 | * @param sc context for the search that should be stopped | ||
1764 | */ | ||
1765 | void | ||
1766 | GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc) | ||
1767 | { | ||
1768 | struct GNUNET_FS_ProgressInfo pi; | ||
1769 | unsigned int i; | ||
1770 | |||
1771 | if (NULL != sc->top) | ||
1772 | GNUNET_FS_end_top (sc->h, sc->top); | ||
1773 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1774 | &search_result_stop, | ||
1775 | sc); | ||
1776 | if (NULL != sc->psearch_result) | ||
1777 | sc->psearch_result->update_search = NULL; | ||
1778 | if (NULL != sc->serialization) | ||
1779 | { | ||
1780 | GNUNET_FS_remove_sync_file_ (sc->h, | ||
1781 | (NULL != sc->psearch_result) | ||
1782 | ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH | ||
1783 | : GNUNET_FS_SYNC_PATH_MASTER_SEARCH, | ||
1784 | sc->serialization); | ||
1785 | GNUNET_FS_remove_sync_dir_ (sc->h, | ||
1786 | (NULL != sc->psearch_result) | ||
1787 | ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH | ||
1788 | : GNUNET_FS_SYNC_PATH_MASTER_SEARCH, | ||
1789 | sc->serialization); | ||
1790 | GNUNET_free (sc->serialization); | ||
1791 | } | ||
1792 | pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED; | ||
1793 | sc->client_info = GNUNET_FS_search_make_status_ (&pi, | ||
1794 | sc->h, | ||
1795 | sc); | ||
1796 | GNUNET_break (NULL == sc->client_info); | ||
1797 | if (NULL != sc->task) | ||
1798 | { | ||
1799 | GNUNET_SCHEDULER_cancel (sc->task); | ||
1800 | sc->task = NULL; | ||
1801 | } | ||
1802 | if (NULL != sc->mq) | ||
1803 | { | ||
1804 | GNUNET_MQ_destroy (sc->mq); | ||
1805 | sc->mq = NULL; | ||
1806 | } | ||
1807 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
1808 | &search_result_free, sc); | ||
1809 | GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map); | ||
1810 | if (NULL != sc->requests) | ||
1811 | { | ||
1812 | GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri)); | ||
1813 | for (i = 0; i < sc->uri->data.ksk.keywordCount; i++) | ||
1814 | { | ||
1815 | GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results); | ||
1816 | GNUNET_free (sc->requests[i].keyword); | ||
1817 | } | ||
1818 | } | ||
1819 | GNUNET_free (sc->requests); | ||
1820 | GNUNET_free (sc->emsg); | ||
1821 | GNUNET_FS_uri_destroy (sc->uri); | ||
1822 | GNUNET_free (sc); | ||
1823 | } | ||
1824 | |||
1825 | |||
1826 | /* end of fs_search.c */ | ||
diff --git a/src/fs/fs_sharetree.c b/src/fs/fs_sharetree.c deleted file mode 100644 index 6c246a3ad..000000000 --- a/src/fs/fs_sharetree.c +++ /dev/null | |||
@@ -1,457 +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 | |||
29 | #include "gnunet_fs_service.h" | ||
30 | #include "gnunet_scheduler_lib.h" | ||
31 | #include <pthread.h> | ||
32 | |||
33 | |||
34 | /** | ||
35 | * Entry for each unique keyword to track how often | ||
36 | * it occurred. Contains the keyword and the counter. | ||
37 | */ | ||
38 | struct KeywordCounter | ||
39 | { | ||
40 | /** | ||
41 | * This is a doubly-linked list | ||
42 | */ | ||
43 | struct KeywordCounter *prev; | ||
44 | |||
45 | /** | ||
46 | * This is a doubly-linked list | ||
47 | */ | ||
48 | struct KeywordCounter *next; | ||
49 | |||
50 | /** | ||
51 | * Keyword that was found. | ||
52 | */ | ||
53 | const char *value; | ||
54 | |||
55 | /** | ||
56 | * How many files have this keyword? | ||
57 | */ | ||
58 | unsigned int count; | ||
59 | }; | ||
60 | |||
61 | |||
62 | /** | ||
63 | * Aggregate information we keep for meta data in each directory. | ||
64 | */ | ||
65 | struct MetaCounter | ||
66 | { | ||
67 | /** | ||
68 | * This is a doubly-linked list | ||
69 | */ | ||
70 | struct MetaCounter *prev; | ||
71 | |||
72 | /** | ||
73 | * This is a doubly-linked list | ||
74 | */ | ||
75 | struct MetaCounter *next; | ||
76 | |||
77 | /** | ||
78 | * Name of the plugin that provided that piece of metadata | ||
79 | */ | ||
80 | const char *plugin_name; | ||
81 | |||
82 | /** | ||
83 | * MIME-type of the metadata itself | ||
84 | */ | ||
85 | const char *data_mime_type; | ||
86 | |||
87 | /** | ||
88 | * The actual meta data. | ||
89 | */ | ||
90 | const char *data; | ||
91 | |||
92 | /** | ||
93 | * Number of bytes in 'data'. | ||
94 | */ | ||
95 | size_t data_size; | ||
96 | |||
97 | /** | ||
98 | * Type of the data | ||
99 | */ | ||
100 | enum EXTRACTOR_MetaType type; | ||
101 | |||
102 | /** | ||
103 | * Format of the data | ||
104 | */ | ||
105 | enum EXTRACTOR_MetaFormat format; | ||
106 | |||
107 | /** | ||
108 | * How many files have meta entries matching this value? | ||
109 | * (type and format do not have to match). | ||
110 | */ | ||
111 | unsigned int count; | ||
112 | }; | ||
113 | |||
114 | |||
115 | /** | ||
116 | * A structure that forms a singly-linked list that serves as a stack | ||
117 | * for metadata-processing function. | ||
118 | */ | ||
119 | struct TrimContext | ||
120 | { | ||
121 | /** | ||
122 | * Map from the hash over the keyword to an 'struct KeywordCounter *' | ||
123 | * counter that says how often this keyword was | ||
124 | * encountered in the current directory. | ||
125 | */ | ||
126 | struct GNUNET_CONTAINER_MultiHashMap *keywordcounter; | ||
127 | |||
128 | /** | ||
129 | * Map from the hash over the metadata to an 'struct MetaCounter *' | ||
130 | * counter that says how often this metadata was | ||
131 | * encountered in the current directory. | ||
132 | */ | ||
133 | struct GNUNET_CONTAINER_MultiHashMap *metacounter; | ||
134 | |||
135 | /** | ||
136 | * Position we are currently manipulating. | ||
137 | */ | ||
138 | struct GNUNET_FS_ShareTreeItem *pos; | ||
139 | |||
140 | /** | ||
141 | * Number of times an item has to be found to be moved to the parent. | ||
142 | */ | ||
143 | unsigned int move_threshold; | ||
144 | }; | ||
145 | |||
146 | |||
147 | /** | ||
148 | * Add the given keyword to the keyword statistics tracker. | ||
149 | * | ||
150 | * @param cls the multihashmap we store the keyword counters in | ||
151 | * @param keyword the keyword to count | ||
152 | * @param is_mandatory ignored | ||
153 | * @return always GNUNET_OK | ||
154 | */ | ||
155 | static int | ||
156 | add_to_keyword_counter (void *cls, const char *keyword, int is_mandatory) | ||
157 | { | ||
158 | struct GNUNET_CONTAINER_MultiHashMap *mcm = cls; | ||
159 | struct KeywordCounter *cnt; | ||
160 | struct GNUNET_HashCode hc; | ||
161 | size_t klen; | ||
162 | |||
163 | klen = strlen (keyword) + 1; | ||
164 | GNUNET_CRYPTO_hash (keyword, klen - 1, &hc); | ||
165 | cnt = GNUNET_CONTAINER_multihashmap_get (mcm, &hc); | ||
166 | if (cnt == NULL) | ||
167 | { | ||
168 | cnt = GNUNET_malloc (sizeof(struct KeywordCounter) + klen); | ||
169 | cnt->value = (const char *) &cnt[1]; | ||
170 | GNUNET_memcpy (&cnt[1], keyword, klen); | ||
171 | GNUNET_assert (GNUNET_OK == | ||
172 | GNUNET_CONTAINER_multihashmap_put (mcm, | ||
173 | &hc, cnt, | ||
174 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
175 | } | ||
176 | cnt->count++; | ||
177 | return GNUNET_OK; | ||
178 | } | ||
179 | |||
180 | |||
181 | /** | ||
182 | * Function called on each meta data item. Increments the | ||
183 | * respective counter. | ||
184 | * | ||
185 | * @param cls the container multihashmap to update | ||
186 | * @param plugin_name name of the plugin that produced this value; | ||
187 | * special values can be used (e.g. '<zlib>' for zlib being | ||
188 | * used in the main libextractor library and yielding | ||
189 | * meta data). | ||
190 | * @param type libextractor-type describing the meta data | ||
191 | * @param format basic format information about data | ||
192 | * @param data_mime_type mime-type of data (not of the original file); | ||
193 | * can be NULL (if mime-type is not known) | ||
194 | * @param data actual meta-data found | ||
195 | * @param data_len number of bytes in data | ||
196 | * @return 0 to continue extracting / iterating | ||
197 | */ | ||
198 | static int | ||
199 | add_to_meta_counter (void *cls, const char *plugin_name, | ||
200 | enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat | ||
201 | format, | ||
202 | const char *data_mime_type, const char *data, size_t | ||
203 | data_len) | ||
204 | { | ||
205 | struct GNUNET_CONTAINER_MultiHashMap *map = cls; | ||
206 | struct GNUNET_HashCode key; | ||
207 | struct MetaCounter *cnt; | ||
208 | |||
209 | GNUNET_CRYPTO_hash (data, data_len, &key); | ||
210 | cnt = GNUNET_CONTAINER_multihashmap_get (map, &key); | ||
211 | if (NULL == cnt) | ||
212 | { | ||
213 | cnt = GNUNET_new (struct MetaCounter); | ||
214 | cnt->data = data; | ||
215 | cnt->data_size = data_len; | ||
216 | cnt->plugin_name = plugin_name; | ||
217 | cnt->type = type; | ||
218 | cnt->format = format; | ||
219 | cnt->data_mime_type = data_mime_type; | ||
220 | GNUNET_assert (GNUNET_OK == | ||
221 | GNUNET_CONTAINER_multihashmap_put (map, | ||
222 | &key, cnt, | ||
223 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
224 | } | ||
225 | cnt->count++; | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | |||
230 | /** | ||
231 | * Remove keywords above the threshold. | ||
232 | * | ||
233 | * @param cls the 'struct TrimContext' with the pos to remove the keywords from | ||
234 | * @param keyword the keyword to check | ||
235 | * @param is_mandatory ignored | ||
236 | * @return always GNUNET_OK | ||
237 | */ | ||
238 | static int | ||
239 | remove_high_frequency_keywords (void *cls, const char *keyword, int | ||
240 | is_mandatory) | ||
241 | { | ||
242 | struct TrimContext *tc = cls; | ||
243 | struct KeywordCounter *counter; | ||
244 | struct GNUNET_HashCode hc; | ||
245 | size_t klen; | ||
246 | |||
247 | klen = strlen (keyword) + 1; | ||
248 | GNUNET_CRYPTO_hash (keyword, klen - 1, &hc); | ||
249 | counter = GNUNET_CONTAINER_multihashmap_get (tc->keywordcounter, &hc); | ||
250 | GNUNET_assert (NULL != counter); | ||
251 | if (counter->count < tc->move_threshold) | ||
252 | return GNUNET_OK; | ||
253 | GNUNET_FS_uri_ksk_remove_keyword (tc->pos->ksk_uri, | ||
254 | counter->value); | ||
255 | return GNUNET_OK; | ||
256 | } | ||
257 | |||
258 | |||
259 | /** | ||
260 | * Move "frequent" keywords over to the target ksk uri, free the | ||
261 | * counters. | ||
262 | * | ||
263 | * @param cls the 'struct TrimContext' | ||
264 | * @param key key of the entry | ||
265 | * @param value the 'struct KeywordCounter' | ||
266 | * @return GNUNET_YES (always) | ||
267 | */ | ||
268 | static int | ||
269 | migrate_and_drop_keywords (void *cls, const struct GNUNET_HashCode *key, | ||
270 | void *value) | ||
271 | { | ||
272 | struct TrimContext *tc = cls; | ||
273 | struct KeywordCounter *counter = value; | ||
274 | |||
275 | if (counter->count >= tc->move_threshold) | ||
276 | { | ||
277 | if (NULL == tc->pos->ksk_uri) | ||
278 | tc->pos->ksk_uri = GNUNET_FS_uri_ksk_create_from_args (1, | ||
279 | &counter->value); | ||
280 | else | ||
281 | GNUNET_FS_uri_ksk_add_keyword (tc->pos->ksk_uri, counter->value, | ||
282 | GNUNET_NO); | ||
283 | } | ||
284 | GNUNET_assert (GNUNET_YES == | ||
285 | GNUNET_CONTAINER_multihashmap_remove (tc->keywordcounter, | ||
286 | key, | ||
287 | counter)); | ||
288 | GNUNET_free (counter); | ||
289 | return GNUNET_YES; | ||
290 | } | ||
291 | |||
292 | |||
293 | /** | ||
294 | * Copy "frequent" metadata items over to the | ||
295 | * target metadata container, free the counters. | ||
296 | * | ||
297 | * @param cls the 'struct TrimContext' | ||
298 | * @param key key of the entry | ||
299 | * @param value the 'struct KeywordCounter' | ||
300 | * @return GNUNET_YES (always) | ||
301 | */ | ||
302 | static int | ||
303 | migrate_and_drop_metadata (void *cls, const struct GNUNET_HashCode *key, | ||
304 | void *value) | ||
305 | { | ||
306 | struct TrimContext *tc = cls; | ||
307 | struct MetaCounter *counter = value; | ||
308 | |||
309 | if (counter->count >= tc->move_threshold) | ||
310 | { | ||
311 | if (NULL == tc->pos->meta) | ||
312 | tc->pos->meta = GNUNET_FS_meta_data_create (); | ||
313 | GNUNET_FS_meta_data_insert (tc->pos->meta, | ||
314 | counter->plugin_name, | ||
315 | counter->type, | ||
316 | counter->format, | ||
317 | counter->data_mime_type, counter->data, | ||
318 | counter->data_size); | ||
319 | } | ||
320 | GNUNET_assert (GNUNET_YES == | ||
321 | GNUNET_CONTAINER_multihashmap_remove (tc->metacounter, | ||
322 | key, | ||
323 | counter)); | ||
324 | GNUNET_free (counter); | ||
325 | return GNUNET_YES; | ||
326 | } | ||
327 | |||
328 | |||
329 | /** | ||
330 | * Process a share item tree, moving frequent keywords up and | ||
331 | * copying frequent metadata up. | ||
332 | * | ||
333 | * @param tc trim context with hash maps to use | ||
334 | * @param tree tree to trim | ||
335 | */ | ||
336 | static void | ||
337 | share_tree_trim (struct TrimContext *tc, | ||
338 | struct GNUNET_FS_ShareTreeItem *tree) | ||
339 | { | ||
340 | struct GNUNET_FS_ShareTreeItem *pos; | ||
341 | unsigned int num_children; | ||
342 | |||
343 | /* first, trim all children */ | ||
344 | num_children = 0; | ||
345 | for (pos = tree->children_head; NULL != pos; pos = pos->next) | ||
346 | { | ||
347 | share_tree_trim (tc, pos); | ||
348 | num_children++; | ||
349 | } | ||
350 | |||
351 | /* consider adding filename to directory meta data */ | ||
352 | if (tree->is_directory == GNUNET_YES) | ||
353 | { | ||
354 | const char *user = getenv ("USER"); | ||
355 | if ((user == NULL) || | ||
356 | (0 != strncasecmp (user, tree->short_filename, strlen (user)))) | ||
357 | { | ||
358 | /* only use filename if it doesn't match $USER */ | ||
359 | if (NULL == tree->meta) | ||
360 | tree->meta = GNUNET_FS_meta_data_create (); | ||
361 | GNUNET_FS_meta_data_insert (tree->meta, "<libgnunetfs>", | ||
362 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME, | ||
363 | EXTRACTOR_METAFORMAT_UTF8, | ||
364 | "text/plain", tree->short_filename, | ||
365 | strlen (tree->short_filename) + 1); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | if (1 >= num_children) | ||
370 | return; /* nothing to trim */ | ||
371 | |||
372 | /* now, count keywords and meta data in children */ | ||
373 | for (pos = tree->children_head; NULL != pos; pos = pos->next) | ||
374 | { | ||
375 | if (NULL != pos->meta) | ||
376 | GNUNET_FS_meta_data_iterate (pos->meta, &add_to_meta_counter, | ||
377 | tc->metacounter); | ||
378 | if (NULL != pos->ksk_uri) | ||
379 | GNUNET_FS_uri_ksk_get_keywords (pos->ksk_uri, &add_to_keyword_counter, | ||
380 | tc->keywordcounter); | ||
381 | } | ||
382 | |||
383 | /* calculate threshold for moving keywords / meta data */ | ||
384 | tc->move_threshold = 1 + (num_children / 2); | ||
385 | |||
386 | /* remove high-frequency keywords from children */ | ||
387 | for (pos = tree->children_head; NULL != pos; pos = pos->next) | ||
388 | { | ||
389 | tc->pos = pos; | ||
390 | if (NULL != pos->ksk_uri) | ||
391 | { | ||
392 | struct GNUNET_FS_Uri *ksk_uri_copy = GNUNET_FS_uri_dup (pos->ksk_uri); | ||
393 | GNUNET_FS_uri_ksk_get_keywords (ksk_uri_copy, | ||
394 | &remove_high_frequency_keywords, tc); | ||
395 | GNUNET_FS_uri_destroy (ksk_uri_copy); | ||
396 | } | ||
397 | } | ||
398 | |||
399 | /* add high-frequency meta data and keywords to parent */ | ||
400 | tc->pos = tree; | ||
401 | GNUNET_CONTAINER_multihashmap_iterate (tc->keywordcounter, | ||
402 | &migrate_and_drop_keywords, | ||
403 | tc); | ||
404 | GNUNET_CONTAINER_multihashmap_iterate (tc->metacounter, | ||
405 | &migrate_and_drop_metadata, | ||
406 | tc); | ||
407 | } | ||
408 | |||
409 | |||
410 | /** | ||
411 | * Process a share item tree, moving frequent keywords up and | ||
412 | * copying frequent metadata up. | ||
413 | * | ||
414 | * @param toplevel toplevel directory in the tree, returned by the scanner | ||
415 | */ | ||
416 | void | ||
417 | GNUNET_FS_share_tree_trim (struct GNUNET_FS_ShareTreeItem *toplevel) | ||
418 | { | ||
419 | struct TrimContext tc; | ||
420 | |||
421 | if (toplevel == NULL) | ||
422 | return; | ||
423 | tc.keywordcounter = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO); | ||
424 | tc.metacounter = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO); | ||
425 | share_tree_trim (&tc, toplevel); | ||
426 | GNUNET_CONTAINER_multihashmap_destroy (tc.keywordcounter); | ||
427 | GNUNET_CONTAINER_multihashmap_destroy (tc.metacounter); | ||
428 | } | ||
429 | |||
430 | |||
431 | /** | ||
432 | * Release memory of a share item tree. | ||
433 | * | ||
434 | * @param toplevel toplevel of the tree to be freed | ||
435 | */ | ||
436 | void | ||
437 | GNUNET_FS_share_tree_free (struct GNUNET_FS_ShareTreeItem *toplevel) | ||
438 | { | ||
439 | struct GNUNET_FS_ShareTreeItem *pos; | ||
440 | |||
441 | while (NULL != (pos = toplevel->children_head)) | ||
442 | GNUNET_FS_share_tree_free (pos); | ||
443 | if (NULL != toplevel->parent) | ||
444 | GNUNET_CONTAINER_DLL_remove (toplevel->parent->children_head, | ||
445 | toplevel->parent->children_tail, | ||
446 | toplevel); | ||
447 | if (NULL != toplevel->meta) | ||
448 | GNUNET_FS_meta_data_destroy (toplevel->meta); | ||
449 | if (NULL != toplevel->ksk_uri) | ||
450 | GNUNET_FS_uri_destroy (toplevel->ksk_uri); | ||
451 | GNUNET_free (toplevel->filename); | ||
452 | GNUNET_free (toplevel->short_filename); | ||
453 | GNUNET_free (toplevel); | ||
454 | } | ||
455 | |||
456 | |||
457 | /* 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 f80a2859c..000000000 --- a/src/fs/fs_test_lib.c +++ /dev/null | |||
@@ -1,630 +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 | */ | ||
40 | struct 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 | */ | ||
112 | struct 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 | */ | ||
181 | static void | ||
182 | report_uri (void *cls) | ||
183 | { | ||
184 | struct TestPublishOperation *po = cls; | ||
185 | |||
186 | GNUNET_FS_publish_stop (po->publish_context); | ||
187 | GNUNET_TESTBED_operation_done (po->fs_op); | ||
188 | po->publish_cont (po->publish_cont_cls, | ||
189 | po->publish_uri, | ||
190 | (GNUNET_YES == po->do_index) | ||
191 | ? po->publish_tmp_file | ||
192 | : NULL); | ||
193 | GNUNET_FS_uri_destroy (po->publish_uri); | ||
194 | if ((GNUNET_YES != po->do_index) && | ||
195 | (NULL != po->publish_tmp_file)) | ||
196 | (void) GNUNET_DISK_directory_remove (po->publish_tmp_file); | ||
197 | GNUNET_free (po->publish_tmp_file); | ||
198 | GNUNET_free (po); | ||
199 | } | ||
200 | |||
201 | |||
202 | /** | ||
203 | * Task scheduled to run when publish operation times out. | ||
204 | * | ||
205 | * @param cls the publish operation context | ||
206 | */ | ||
207 | static void | ||
208 | publish_timeout (void *cls) | ||
209 | { | ||
210 | struct TestPublishOperation *po = cls; | ||
211 | |||
212 | po->publish_timeout_task = NULL; | ||
213 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
214 | "Timeout while trying to publish data\n"); | ||
215 | GNUNET_TESTBED_operation_done (po->fs_op); | ||
216 | GNUNET_FS_publish_stop (po->publish_context); | ||
217 | po->publish_cont (po->publish_cont_cls, NULL, NULL); | ||
218 | (void) GNUNET_DISK_directory_remove (po->publish_tmp_file); | ||
219 | GNUNET_free (po->publish_tmp_file); | ||
220 | GNUNET_free (po); | ||
221 | } | ||
222 | |||
223 | |||
224 | /** | ||
225 | * Progress callback for file-sharing events while publishing. | ||
226 | * | ||
227 | * @param cls the publish operation context | ||
228 | * @param info information about the event | ||
229 | */ | ||
230 | static void * | ||
231 | publish_progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) | ||
232 | { | ||
233 | struct TestPublishOperation *po = cls; | ||
234 | |||
235 | switch (info->status) | ||
236 | { | ||
237 | case GNUNET_FS_STATUS_PUBLISH_COMPLETED: | ||
238 | GNUNET_SCHEDULER_cancel (po->publish_timeout_task); | ||
239 | po->publish_timeout_task = NULL; | ||
240 | po->publish_uri = | ||
241 | GNUNET_FS_uri_dup (info->value.publish.specifics.completed.chk_uri); | ||
242 | GNUNET_SCHEDULER_add_now (&report_uri, | ||
243 | po); | ||
244 | break; | ||
245 | |||
246 | case GNUNET_FS_STATUS_PUBLISH_PROGRESS: | ||
247 | if (po->verbose) | ||
248 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Publishing at %llu/%llu bytes\n", | ||
249 | (unsigned long long) info->value.publish.completed, | ||
250 | (unsigned long long) info->value.publish.size); | ||
251 | break; | ||
252 | |||
253 | case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY: | ||
254 | break; | ||
255 | |||
256 | case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: | ||
257 | if (po->verbose) | ||
258 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Download at %llu/%llu bytes\n", | ||
259 | (unsigned long long) info->value.download.completed, | ||
260 | (unsigned long long) info->value.download.size); | ||
261 | break; | ||
262 | |||
263 | default: | ||
264 | break; | ||
265 | } | ||
266 | return NULL; | ||
267 | } | ||
268 | |||
269 | |||
270 | /** | ||
271 | * Generate test data for publishing test. | ||
272 | * | ||
273 | * @param cls pointer to uint32_t with publishing seed | ||
274 | * @param offset offset to generate data for | ||
275 | * @param max maximum number of bytes to generate | ||
276 | * @param buf where to write generated data | ||
277 | * @param emsg where to store error message (unused) | ||
278 | * @return number of bytes written to buf | ||
279 | */ | ||
280 | static size_t | ||
281 | file_generator (void *cls, | ||
282 | uint64_t offset, | ||
283 | size_t max, | ||
284 | void *buf, | ||
285 | char **emsg) | ||
286 | { | ||
287 | uint32_t *publish_seed = cls; | ||
288 | uint64_t pos; | ||
289 | uint8_t *cbuf = buf; | ||
290 | int mod; | ||
291 | |||
292 | if (emsg != NULL) | ||
293 | *emsg = NULL; | ||
294 | if (buf == NULL) | ||
295 | return 0; | ||
296 | for (pos = 0; pos < 8; pos++) | ||
297 | cbuf[pos] = (uint8_t) (offset >> pos * 8); | ||
298 | for (pos = 8; pos < max; pos++) | ||
299 | { | ||
300 | mod = (255 - (offset / 1024 / 32)); | ||
301 | if (mod == 0) | ||
302 | mod = 1; | ||
303 | cbuf[pos] = (uint8_t) ((offset * (*publish_seed)) % mod); | ||
304 | } | ||
305 | return max; | ||
306 | } | ||
307 | |||
308 | |||
309 | /** | ||
310 | * Connect adapter for publishing operation. | ||
311 | * | ||
312 | * @param cls the 'struct TestPublishOperation' | ||
313 | * @param cfg configuration of the peer to connect to; will be available until | ||
314 | * GNUNET_TESTBED_operation_done() is called on the operation returned | ||
315 | * from GNUNET_TESTBED_service_connect() | ||
316 | * @return service handle to return in 'op_result', NULL on error | ||
317 | */ | ||
318 | static void * | ||
319 | publish_connect_adapter (void *cls, | ||
320 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
321 | { | ||
322 | struct TestPublishOperation *po = cls; | ||
323 | |||
324 | return GNUNET_FS_start (cfg, | ||
325 | "fs-test-publish", | ||
326 | &publish_progress_cb, po, | ||
327 | GNUNET_FS_FLAGS_NONE, | ||
328 | GNUNET_FS_OPTIONS_END); | ||
329 | } | ||
330 | |||
331 | |||
332 | /** | ||
333 | * Adapter function called to destroy connection to file-sharing service. | ||
334 | * | ||
335 | * @param cls the 'struct GNUNET_FS_Handle' | ||
336 | * @param op_result unused (different for publish/download!) | ||
337 | */ | ||
338 | static void | ||
339 | fs_disconnect_adapter (void *cls, | ||
340 | void *op_result) | ||
341 | { | ||
342 | struct GNUNET_FS_Handle *fs = op_result; | ||
343 | |||
344 | GNUNET_FS_stop (fs); | ||
345 | } | ||
346 | |||
347 | |||
348 | /** | ||
349 | * Callback to be called when testbed has connected to the fs service | ||
350 | * | ||
351 | * @param cls the 'struct TestPublishOperation' | ||
352 | * @param op the operation that has been finished | ||
353 | * @param ca_result the 'struct GNUNET_FS_Handle ' (NULL on error) | ||
354 | * @param emsg error message in case the operation has failed; will be NULL if | ||
355 | * operation has executed successfully. | ||
356 | */ | ||
357 | static void | ||
358 | publish_fs_connect_complete_cb (void *cls, | ||
359 | struct GNUNET_TESTBED_Operation *op, | ||
360 | void *ca_result, | ||
361 | const char *emsg) | ||
362 | { | ||
363 | struct TestPublishOperation *po = cls; | ||
364 | struct GNUNET_FS_FileInformation *fi; | ||
365 | struct GNUNET_DISK_FileHandle *fh; | ||
366 | char *em; | ||
367 | uint64_t off; | ||
368 | char buf[DBLOCK_SIZE]; | ||
369 | size_t bsize; | ||
370 | struct GNUNET_FS_BlockOptions bo; | ||
371 | |||
372 | if (NULL == ca_result) | ||
373 | { | ||
374 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
375 | "Failed to connect to FS for publishing: %s\n", emsg); | ||
376 | po->publish_cont (po->publish_cont_cls, | ||
377 | NULL, NULL); | ||
378 | GNUNET_TESTBED_operation_done (po->fs_op); | ||
379 | GNUNET_free (po); | ||
380 | return; | ||
381 | } | ||
382 | po->fs = ca_result; | ||
383 | |||
384 | bo.expiration_time = GNUNET_TIME_relative_to_absolute (CONTENT_LIFETIME); | ||
385 | bo.anonymity_level = po->anonymity; | ||
386 | bo.content_priority = 42; | ||
387 | bo.replication_level = 1; | ||
388 | if (GNUNET_YES == po->do_index) | ||
389 | { | ||
390 | po->publish_tmp_file = GNUNET_DISK_mktemp ("fs-test-publish-index"); | ||
391 | GNUNET_assert (po->publish_tmp_file != NULL); | ||
392 | fh = GNUNET_DISK_file_open (po->publish_tmp_file, | ||
393 | GNUNET_DISK_OPEN_WRITE | ||
394 | | GNUNET_DISK_OPEN_CREATE, | ||
395 | GNUNET_DISK_PERM_USER_READ | ||
396 | | GNUNET_DISK_PERM_USER_WRITE); | ||
397 | GNUNET_assert (NULL != fh); | ||
398 | off = 0; | ||
399 | while (off < po->size) | ||
400 | { | ||
401 | bsize = GNUNET_MIN (sizeof(buf), po->size - off); | ||
402 | emsg = NULL; | ||
403 | GNUNET_assert (bsize == file_generator (&po->publish_seed, off, bsize, | ||
404 | buf, &em)); | ||
405 | GNUNET_assert (em == NULL); | ||
406 | GNUNET_assert (bsize == GNUNET_DISK_file_write (fh, buf, bsize)); | ||
407 | off += bsize; | ||
408 | } | ||
409 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh)); | ||
410 | fi = GNUNET_FS_file_information_create_from_file (po->fs, po, | ||
411 | po->publish_tmp_file, | ||
412 | NULL, NULL, po->do_index, | ||
413 | &bo); | ||
414 | GNUNET_assert (NULL != fi); | ||
415 | } | ||
416 | else | ||
417 | { | ||
418 | fi = GNUNET_FS_file_information_create_from_reader (po->fs, po, | ||
419 | po->size, | ||
420 | &file_generator, | ||
421 | &po->publish_seed, | ||
422 | NULL, NULL, | ||
423 | po->do_index, &bo); | ||
424 | GNUNET_assert (NULL != fi); | ||
425 | } | ||
426 | po->publish_context = | ||
427 | GNUNET_FS_publish_start (po->fs, fi, NULL, NULL, NULL, | ||
428 | GNUNET_FS_PUBLISH_OPTION_NONE); | ||
429 | } | ||
430 | |||
431 | |||
432 | void | ||
433 | GNUNET_FS_TEST_publish (struct GNUNET_TESTBED_Peer *peer, | ||
434 | struct GNUNET_TIME_Relative timeout, uint32_t anonymity, | ||
435 | int do_index, uint64_t size, uint32_t seed, | ||
436 | unsigned int verbose, | ||
437 | GNUNET_FS_TEST_UriContinuation cont, void *cont_cls) | ||
438 | { | ||
439 | struct TestPublishOperation *po; | ||
440 | |||
441 | po = GNUNET_new (struct TestPublishOperation); | ||
442 | po->publish_cont = cont; | ||
443 | po->publish_cont_cls = cont_cls; | ||
444 | po->publish_seed = seed; | ||
445 | po->anonymity = anonymity; | ||
446 | po->size = size; | ||
447 | po->verbose = verbose; | ||
448 | po->do_index = do_index; | ||
449 | po->fs_op = GNUNET_TESTBED_service_connect (po, | ||
450 | peer, | ||
451 | "fs", | ||
452 | &publish_fs_connect_complete_cb, | ||
453 | po, | ||
454 | &publish_connect_adapter, | ||
455 | &fs_disconnect_adapter, | ||
456 | po); | ||
457 | po->publish_timeout_task = | ||
458 | GNUNET_SCHEDULER_add_delayed (timeout, &publish_timeout, po); | ||
459 | } | ||
460 | |||
461 | |||
462 | /* ************************** download ************************ */ | ||
463 | |||
464 | |||
465 | /** | ||
466 | * Task scheduled to run when download operation times out. | ||
467 | * | ||
468 | * @param cls the download operation context | ||
469 | */ | ||
470 | static void | ||
471 | download_timeout (void *cls) | ||
472 | { | ||
473 | struct TestDownloadOperation *dop = cls; | ||
474 | |||
475 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
476 | "Timeout while trying to download file\n"); | ||
477 | dop->download_timeout_task = NULL; | ||
478 | GNUNET_FS_download_stop (dop->download_context, | ||
479 | GNUNET_YES); | ||
480 | GNUNET_SCHEDULER_add_now (dop->download_cont, | ||
481 | dop->download_cont_cls); | ||
482 | GNUNET_TESTBED_operation_done (dop->fs_op); | ||
483 | GNUNET_FS_uri_destroy (dop->uri); | ||
484 | GNUNET_free (dop); | ||
485 | } | ||
486 | |||
487 | |||
488 | /** | ||
489 | * Task scheduled to report on the completion of our download operation. | ||
490 | * | ||
491 | * @param cls the download operation context | ||
492 | */ | ||
493 | static void | ||
494 | report_success (void *cls) | ||
495 | { | ||
496 | struct TestDownloadOperation *dop = cls; | ||
497 | |||
498 | GNUNET_FS_download_stop (dop->download_context, | ||
499 | GNUNET_YES); | ||
500 | GNUNET_SCHEDULER_add_now (dop->download_cont, | ||
501 | dop->download_cont_cls); | ||
502 | GNUNET_TESTBED_operation_done (dop->fs_op); | ||
503 | GNUNET_FS_uri_destroy (dop->uri); | ||
504 | GNUNET_free (dop); | ||
505 | } | ||
506 | |||
507 | |||
508 | /** | ||
509 | * Progress callback for file-sharing events while downloading. | ||
510 | * | ||
511 | * @param cls the download operation context | ||
512 | * @param info information about the event | ||
513 | */ | ||
514 | static void * | ||
515 | download_progress_cb (void *cls, | ||
516 | const struct GNUNET_FS_ProgressInfo *info) | ||
517 | { | ||
518 | struct TestDownloadOperation *dop = cls; | ||
519 | |||
520 | switch (info->status) | ||
521 | { | ||
522 | case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: | ||
523 | if (dop->verbose) | ||
524 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
525 | "Download at %llu/%llu bytes\n", | ||
526 | (unsigned long long) info->value.download.completed, | ||
527 | (unsigned long long) info->value.download.size); | ||
528 | break; | ||
529 | |||
530 | case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: | ||
531 | GNUNET_SCHEDULER_cancel (dop->download_timeout_task); | ||
532 | dop->download_timeout_task = NULL; | ||
533 | GNUNET_SCHEDULER_add_now (&report_success, dop); | ||
534 | break; | ||
535 | |||
536 | case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: | ||
537 | case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: | ||
538 | break; | ||
539 | |||
540 | /* FIXME: monitor data correctness during download progress */ | ||
541 | /* FIXME: do performance reports given sufficient verbosity */ | ||
542 | /* FIXME: advance timeout task to "immediate" on error */ | ||
543 | default: | ||
544 | break; | ||
545 | } | ||
546 | return NULL; | ||
547 | } | ||
548 | |||
549 | |||
550 | /** | ||
551 | * Connect adapter for download operation. | ||
552 | * | ||
553 | * @param cls the 'struct TestDownloadOperation' | ||
554 | * @param cfg configuration of the peer to connect to; will be available until | ||
555 | * GNUNET_TESTBED_operation_done() is called on the operation returned | ||
556 | * from GNUNET_TESTBED_service_connect() | ||
557 | * @return service handle to return in 'op_result', NULL on error | ||
558 | */ | ||
559 | static void * | ||
560 | download_connect_adapter (void *cls, | ||
561 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
562 | { | ||
563 | struct TestPublishOperation *po = cls; | ||
564 | |||
565 | return GNUNET_FS_start (cfg, | ||
566 | "fs-test-download", | ||
567 | &download_progress_cb, po, | ||
568 | GNUNET_FS_FLAGS_NONE, | ||
569 | GNUNET_FS_OPTIONS_END); | ||
570 | } | ||
571 | |||
572 | |||
573 | /** | ||
574 | * Callback to be called when testbed has connected to the fs service | ||
575 | * | ||
576 | * @param cls the 'struct TestPublishOperation' | ||
577 | * @param op the operation that has been finished | ||
578 | * @param ca_result the 'struct GNUNET_FS_Handle ' (NULL on error) | ||
579 | * @param emsg error message in case the operation has failed; will be NULL if | ||
580 | * operation has executed successfully. | ||
581 | */ | ||
582 | static void | ||
583 | download_fs_connect_complete_cb (void *cls, | ||
584 | struct GNUNET_TESTBED_Operation *op, | ||
585 | void *ca_result, | ||
586 | const char *emsg) | ||
587 | { | ||
588 | struct TestDownloadOperation *dop = cls; | ||
589 | |||
590 | dop->fs = ca_result; | ||
591 | GNUNET_assert (NULL != dop->fs); | ||
592 | dop->download_context = | ||
593 | GNUNET_FS_download_start (dop->fs, dop->uri, NULL, NULL, NULL, 0, dop->size, | ||
594 | dop->anonymity, GNUNET_FS_DOWNLOAD_OPTION_NONE, | ||
595 | NULL, NULL); | ||
596 | } | ||
597 | |||
598 | |||
599 | void | ||
600 | GNUNET_FS_TEST_download (struct GNUNET_TESTBED_Peer *peer, | ||
601 | struct GNUNET_TIME_Relative timeout, | ||
602 | uint32_t anonymity, uint32_t seed, | ||
603 | const struct GNUNET_FS_Uri *uri, unsigned int verbose, | ||
604 | GNUNET_SCHEDULER_TaskCallback cont, void *cont_cls) | ||
605 | { | ||
606 | struct TestDownloadOperation *dop; | ||
607 | |||
608 | dop = GNUNET_new (struct TestDownloadOperation); | ||
609 | dop->uri = GNUNET_FS_uri_dup (uri); | ||
610 | dop->size = GNUNET_FS_uri_chk_get_file_size (uri); | ||
611 | dop->verbose = verbose; | ||
612 | dop->anonymity = anonymity; | ||
613 | dop->download_cont = cont; | ||
614 | dop->download_cont_cls = cont_cls; | ||
615 | dop->download_seed = seed; | ||
616 | |||
617 | dop->fs_op = GNUNET_TESTBED_service_connect (dop, | ||
618 | peer, | ||
619 | "fs", | ||
620 | &download_fs_connect_complete_cb, | ||
621 | dop, | ||
622 | &download_connect_adapter, | ||
623 | &fs_disconnect_adapter, | ||
624 | dop); | ||
625 | dop->download_timeout_task = | ||
626 | GNUNET_SCHEDULER_add_delayed (timeout, &download_timeout, dop); | ||
627 | } | ||
628 | |||
629 | |||
630 | /* 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 | */ | ||
45 | typedef 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 | */ | ||
66 | void | ||
67 | GNUNET_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 | */ | ||
91 | void | ||
92 | GNUNET_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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-fs-test-lib/ | ||
4 | |||
5 | [ats] | ||
6 | WAN_QUOTA_IN = 3932160 | ||
7 | WAN_QUOTA_OUT = 3932160 | ||
8 | |||
9 | [datastore] | ||
10 | QUOTA = 2 GB | ||
11 | #PLUGIN = heap | ||
12 | # | ||
13 | [fs] | ||
14 | IMMEDIATE_START = YES | ||
15 | |||
16 | [testbed] | ||
17 | OVERLAY_TOPOLOGY = CLIQUE | ||
diff --git a/src/fs/fs_tree.c b/src/fs/fs_tree.c deleted file mode 100644 index 65f589966..000000000 --- a/src/fs/fs_tree.c +++ /dev/null | |||
@@ -1,452 +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 | */ | ||
35 | struct 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 | */ | ||
124 | unsigned int | ||
125 | GNUNET_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 | */ | ||
155 | uint64_t | ||
156 | GNUNET_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 | */ | ||
182 | static uint16_t | ||
183 | GNUNET_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 | size_t | ||
211 | GNUNET_FS_tree_calculate_block_size (uint64_t fsize, uint64_t offset, | ||
212 | unsigned int depth) | ||
213 | { | ||
214 | size_t ret; | ||
215 | uint64_t rsize; | ||
216 | uint64_t epos; | ||
217 | unsigned int chks; | ||
218 | |||
219 | GNUNET_assert (fsize > 0); | ||
220 | GNUNET_assert (offset <= fsize); | ||
221 | if (depth == 0) | ||
222 | { | ||
223 | ret = DBLOCK_SIZE; | ||
224 | if ((offset + ret > fsize) || (offset + ret < offset)) | ||
225 | ret = (size_t) (fsize - offset); | ||
226 | return ret; | ||
227 | } | ||
228 | |||
229 | rsize = GNUNET_FS_tree_compute_tree_size (depth - 1); | ||
230 | epos = offset + rsize * CHK_PER_INODE; | ||
231 | if ((epos < offset) || (epos > fsize)) | ||
232 | epos = fsize; | ||
233 | /* round up when computing #CHKs in our IBlock */ | ||
234 | chks = (epos - offset + rsize - 1) / rsize; | ||
235 | GNUNET_assert (chks <= CHK_PER_INODE); | ||
236 | return chks * sizeof(struct ContentHashKey); | ||
237 | } | ||
238 | |||
239 | |||
240 | /** | ||
241 | * Initialize a tree encoder. This function will call @a proc and | ||
242 | * "progress" on each block in the tree. Once all blocks have been | ||
243 | * processed, "cont" will be scheduled. The @a reader will be called | ||
244 | * to obtain the (plaintext) blocks for the file. Note that this | ||
245 | * function will not actually call @a proc. The client must | ||
246 | * call #GNUNET_FS_tree_encoder_next to trigger encryption (and | ||
247 | * calling of @a proc) for the each block. | ||
248 | * | ||
249 | * @param h the global FS context | ||
250 | * @param size overall size of the file to encode | ||
251 | * @param cls closure for reader, proc, progress and cont | ||
252 | * @param reader function to call to read plaintext data | ||
253 | * @param proc function to call on each encrypted block | ||
254 | * @param progress function to call with progress information | ||
255 | * @param cont function to call when done | ||
256 | */ | ||
257 | struct GNUNET_FS_TreeEncoder * | ||
258 | GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h, uint64_t size, | ||
259 | void *cls, | ||
260 | GNUNET_FS_DataReader reader, | ||
261 | GNUNET_FS_TreeBlockProcessor proc, | ||
262 | GNUNET_FS_TreeProgressCallback progress, | ||
263 | GNUNET_SCHEDULER_TaskCallback cont) | ||
264 | { | ||
265 | struct GNUNET_FS_TreeEncoder *te; | ||
266 | |||
267 | te = GNUNET_new (struct GNUNET_FS_TreeEncoder); | ||
268 | te->h = h; | ||
269 | te->size = size; | ||
270 | te->cls = cls; | ||
271 | te->reader = reader; | ||
272 | te->proc = proc; | ||
273 | te->progress = progress; | ||
274 | te->cont = cont; | ||
275 | te->chk_tree_depth = GNUNET_FS_compute_depth (size); | ||
276 | te->chk_tree | ||
277 | = GNUNET_new_array (te->chk_tree_depth * CHK_PER_INODE, | ||
278 | struct ContentHashKey); | ||
279 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
280 | "Created tree encoder for file with %llu bytes and depth %u\n", | ||
281 | (unsigned long long) size, | ||
282 | te->chk_tree_depth); | ||
283 | return te; | ||
284 | } | ||
285 | |||
286 | |||
287 | /** | ||
288 | * Compute the offset of the CHK for the | ||
289 | * current block in the IBlock above. | ||
290 | * | ||
291 | * @param depth depth of the IBlock in the tree (aka overall | ||
292 | * number of tree levels minus depth); 0 == DBlock | ||
293 | * @param end_offset current offset in the overall file, | ||
294 | * at the *beginning* of the block for DBLOCKs (depth==0), | ||
295 | * otherwise at the *end* of the block (exclusive) | ||
296 | * @return (array of CHKs') offset in the above IBlock | ||
297 | */ | ||
298 | static unsigned int | ||
299 | compute_chk_offset (unsigned int depth, uint64_t end_offset) | ||
300 | { | ||
301 | uint64_t bds; | ||
302 | unsigned int ret; | ||
303 | |||
304 | bds = GNUNET_FS_tree_compute_tree_size (depth); | ||
305 | if (depth > 0) | ||
306 | end_offset--; /* round down since for depth > 0 offset is at the END of the block */ | ||
307 | ret = end_offset / bds; | ||
308 | return ret % CHK_PER_INODE; | ||
309 | } | ||
310 | |||
311 | |||
312 | /** | ||
313 | * Encrypt the next block of the file (and call proc and progress | ||
314 | * accordingly; or of course "cont" if we have already completed | ||
315 | * encoding of the entire file). | ||
316 | * | ||
317 | * @param te tree encoder to use | ||
318 | */ | ||
319 | void | ||
320 | GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder *te) | ||
321 | { | ||
322 | struct ContentHashKey *mychk; | ||
323 | const void *pt_block; | ||
324 | uint16_t pt_size; | ||
325 | char iob[DBLOCK_SIZE]; | ||
326 | char enc[DBLOCK_SIZE]; | ||
327 | struct GNUNET_CRYPTO_SymmetricSessionKey sk; | ||
328 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
329 | unsigned int off; | ||
330 | |||
331 | GNUNET_assert (GNUNET_NO == te->in_next); | ||
332 | te->in_next = GNUNET_YES; | ||
333 | if (te->chk_tree_depth == te->current_depth) | ||
334 | { | ||
335 | off = CHK_PER_INODE * (te->chk_tree_depth - 1); | ||
336 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TE done, reading CHK `%s' from %u\n", | ||
337 | GNUNET_h2s (&te->chk_tree[off].query), off); | ||
338 | te->uri = GNUNET_new (struct GNUNET_FS_Uri); | ||
339 | te->uri->type = GNUNET_FS_URI_CHK; | ||
340 | te->uri->data.chk.chk = te->chk_tree[off]; | ||
341 | te->uri->data.chk.file_length = GNUNET_htonll (te->size); | ||
342 | te->in_next = GNUNET_NO; | ||
343 | te->cont (te->cls); | ||
344 | return; | ||
345 | } | ||
346 | if (0 == te->current_depth) | ||
347 | { | ||
348 | /* read DBLOCK */ | ||
349 | pt_size = GNUNET_MIN (DBLOCK_SIZE, te->size - te->publish_offset); | ||
350 | if (pt_size != | ||
351 | te->reader (te->cls, te->publish_offset, pt_size, iob, &te->emsg)) | ||
352 | { | ||
353 | te->in_next = GNUNET_NO; | ||
354 | te->cont (te->cls); | ||
355 | return; | ||
356 | } | ||
357 | pt_block = iob; | ||
358 | } | ||
359 | else | ||
360 | { | ||
361 | pt_size = | ||
362 | GNUNET_FS_tree_compute_iblock_size (te->current_depth, | ||
363 | te->publish_offset); | ||
364 | pt_block = &te->chk_tree[(te->current_depth - 1) * CHK_PER_INODE]; | ||
365 | } | ||
366 | off = compute_chk_offset (te->current_depth, te->publish_offset); | ||
367 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
368 | "TE is at offset %llu and depth %u with block size %u and target-CHK-offset %u\n", | ||
369 | (unsigned long long) te->publish_offset, te->current_depth, | ||
370 | (unsigned int) pt_size, (unsigned int) off); | ||
371 | mychk = &te->chk_tree[te->current_depth * CHK_PER_INODE + off]; | ||
372 | GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key); | ||
373 | GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv); | ||
374 | GNUNET_CRYPTO_symmetric_encrypt (pt_block, pt_size, &sk, &iv, enc); | ||
375 | GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query); | ||
376 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
377 | "TE calculates query to be `%s', stored at %u\n", | ||
378 | GNUNET_h2s (&mychk->query), | ||
379 | te->current_depth * CHK_PER_INODE + off); | ||
380 | if (NULL != te->proc) | ||
381 | te->proc (te->cls, mychk, te->publish_offset, te->current_depth, | ||
382 | (0 == | ||
383 | te->current_depth) ? GNUNET_BLOCK_TYPE_FS_DBLOCK : | ||
384 | GNUNET_BLOCK_TYPE_FS_IBLOCK, enc, pt_size); | ||
385 | if (NULL != te->progress) | ||
386 | te->progress (te->cls, te->publish_offset, pt_block, pt_size, | ||
387 | te->current_depth); | ||
388 | if (0 == te->current_depth) | ||
389 | { | ||
390 | te->publish_offset += pt_size; | ||
391 | if ((te->publish_offset == te->size) || | ||
392 | (0 == te->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE))) | ||
393 | te->current_depth++; | ||
394 | } | ||
395 | else | ||
396 | { | ||
397 | if ((off == CHK_PER_INODE) || (te->publish_offset == te->size)) | ||
398 | te->current_depth++; | ||
399 | else | ||
400 | te->current_depth = 0; | ||
401 | } | ||
402 | te->in_next = GNUNET_NO; | ||
403 | } | ||
404 | |||
405 | |||
406 | /** | ||
407 | * Get the resulting URI from the encoding. | ||
408 | * | ||
409 | * @param te the tree encoder to clean up | ||
410 | * @return uri set to the resulting URI (if encoding finished), NULL otherwise | ||
411 | */ | ||
412 | struct GNUNET_FS_Uri * | ||
413 | GNUNET_FS_tree_encoder_get_uri (struct GNUNET_FS_TreeEncoder *te) | ||
414 | { | ||
415 | if (NULL != te->uri) | ||
416 | return GNUNET_FS_uri_dup (te->uri); | ||
417 | return NULL; | ||
418 | } | ||
419 | |||
420 | |||
421 | /** | ||
422 | * Clean up a tree encoder and return information | ||
423 | * about possible errors. | ||
424 | * | ||
425 | * @param te the tree encoder to clean up | ||
426 | * @param emsg set to an error message (if an error occurred | ||
427 | * within the tree encoder; if this function is called | ||
428 | * prior to completion and prior to an internal error, | ||
429 | * both "*emsg" will be set to NULL). | ||
430 | */ | ||
431 | void | ||
432 | GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder *te, | ||
433 | char **emsg) | ||
434 | { | ||
435 | if (NULL != te->reader) | ||
436 | { | ||
437 | (void) te->reader (te->cls, UINT64_MAX, 0, 0, NULL); | ||
438 | te->reader = NULL; | ||
439 | } | ||
440 | GNUNET_assert (GNUNET_NO == te->in_next); | ||
441 | if (NULL != te->uri) | ||
442 | GNUNET_FS_uri_destroy (te->uri); | ||
443 | if (emsg != NULL) | ||
444 | *emsg = te->emsg; | ||
445 | else | ||
446 | GNUNET_free (te->emsg); | ||
447 | GNUNET_free (te->chk_tree); | ||
448 | GNUNET_free (te); | ||
449 | } | ||
450 | |||
451 | |||
452 | /* 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 | */ | ||
43 | unsigned int | ||
44 | GNUNET_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 | */ | ||
56 | uint64_t | ||
57 | GNUNET_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 | */ | ||
71 | size_t | ||
72 | GNUNET_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 | */ | ||
80 | struct 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 | */ | ||
97 | typedef 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 | */ | ||
116 | typedef 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 | */ | ||
140 | struct GNUNET_FS_TreeEncoder * | ||
141 | GNUNET_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 | */ | ||
156 | void | ||
157 | GNUNET_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 | */ | ||
166 | struct GNUNET_FS_Uri * | ||
167 | GNUNET_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 | */ | ||
180 | void | ||
181 | GNUNET_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 | */ | ||
196 | void | ||
197 | GNUNET_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 | */ | ||
209 | void | ||
210 | GNUNET_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 68ba667c4..000000000 --- a/src/fs/fs_unindex.c +++ /dev/null | |||
@@ -1,902 +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 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | #include "gnunet_protocols.h" | ||
32 | #include "fs_api.h" | ||
33 | #include "fs_tree.h" | ||
34 | #include "block_fs.h" | ||
35 | #include "fs_publish_ublock.h" | ||
36 | |||
37 | |||
38 | /** | ||
39 | * Function called by the tree encoder to obtain | ||
40 | * a block of plaintext data (for the lowest level | ||
41 | * of the tree). | ||
42 | * | ||
43 | * @param cls our publishing context | ||
44 | * @param offset identifies which block to get | ||
45 | * @param max (maximum) number of bytes to get; returning | ||
46 | * fewer will also cause errors | ||
47 | * @param buf where to copy the plaintext buffer | ||
48 | * @param emsg location to store an error message (on error) | ||
49 | * @return number of bytes copied to buf, 0 on error | ||
50 | */ | ||
51 | static size_t | ||
52 | unindex_reader (void *cls, | ||
53 | uint64_t offset, | ||
54 | size_t max, | ||
55 | void *buf, | ||
56 | char **emsg) | ||
57 | { | ||
58 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
59 | size_t pt_size; | ||
60 | |||
61 | pt_size = GNUNET_MIN (max, uc->file_size - offset); | ||
62 | if (offset != GNUNET_DISK_file_seek (uc->fh, offset, GNUNET_DISK_SEEK_SET)) | ||
63 | { | ||
64 | *emsg = GNUNET_strdup (_ ("Failed to find given position in file")); | ||
65 | return 0; | ||
66 | } | ||
67 | if (pt_size != GNUNET_DISK_file_read (uc->fh, buf, pt_size)) | ||
68 | { | ||
69 | *emsg = GNUNET_strdup (_ ("Failed to read file")); | ||
70 | return 0; | ||
71 | } | ||
72 | return pt_size; | ||
73 | } | ||
74 | |||
75 | |||
76 | /** | ||
77 | * Fill in all of the generic fields for | ||
78 | * an unindex event and call the callback. | ||
79 | * | ||
80 | * @param pi structure to fill in | ||
81 | * @param uc overall unindex context | ||
82 | * @param offset where we are in the file (for progress) | ||
83 | */ | ||
84 | void | ||
85 | GNUNET_FS_unindex_make_status_ (struct GNUNET_FS_ProgressInfo *pi, | ||
86 | struct GNUNET_FS_UnindexContext *uc, | ||
87 | uint64_t offset) | ||
88 | { | ||
89 | pi->value.unindex.uc = uc; | ||
90 | pi->value.unindex.cctx = uc->client_info; | ||
91 | pi->value.unindex.filename = uc->filename; | ||
92 | pi->value.unindex.size = uc->file_size; | ||
93 | pi->value.unindex.eta = | ||
94 | GNUNET_TIME_calculate_eta (uc->start_time, offset, uc->file_size); | ||
95 | pi->value.unindex.duration = | ||
96 | GNUNET_TIME_absolute_get_duration (uc->start_time); | ||
97 | pi->value.unindex.completed = offset; | ||
98 | pi->fsh = uc->h; | ||
99 | uc->client_info = uc->h->upcb (uc->h->upcb_cls, pi); | ||
100 | } | ||
101 | |||
102 | |||
103 | /** | ||
104 | * Function called with information about our | ||
105 | * progress in computing the tree encoding. | ||
106 | * | ||
107 | * @param cls closure | ||
108 | * @param offset where are we in the file | ||
109 | * @param pt_block plaintext of the currently processed block | ||
110 | * @param pt_size size of pt_block | ||
111 | * @param depth depth of the block in the tree, 0 for DBLOCK | ||
112 | */ | ||
113 | static void | ||
114 | unindex_progress (void *cls, | ||
115 | uint64_t offset, | ||
116 | const void *pt_block, | ||
117 | size_t pt_size, | ||
118 | unsigned int depth) | ||
119 | { | ||
120 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
121 | struct GNUNET_FS_ProgressInfo pi; | ||
122 | |||
123 | pi.status = GNUNET_FS_STATUS_UNINDEX_PROGRESS; | ||
124 | pi.value.unindex.specifics.progress.data = pt_block; | ||
125 | pi.value.unindex.specifics.progress.offset = offset; | ||
126 | pi.value.unindex.specifics.progress.data_len = pt_size; | ||
127 | pi.value.unindex.specifics.progress.depth = depth; | ||
128 | GNUNET_FS_unindex_make_status_ (&pi, uc, offset); | ||
129 | } | ||
130 | |||
131 | |||
132 | /** | ||
133 | * We've encountered an error during | ||
134 | * unindexing. Signal the client. | ||
135 | * | ||
136 | * @param uc context for the failed unindexing operation | ||
137 | */ | ||
138 | static void | ||
139 | signal_unindex_error (struct GNUNET_FS_UnindexContext *uc) | ||
140 | { | ||
141 | struct GNUNET_FS_ProgressInfo pi; | ||
142 | |||
143 | pi.status = GNUNET_FS_STATUS_UNINDEX_ERROR; | ||
144 | pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL; | ||
145 | pi.value.unindex.specifics.error.message = uc->emsg; | ||
146 | GNUNET_FS_unindex_make_status_ (&pi, uc, 0); | ||
147 | } | ||
148 | |||
149 | |||
150 | /** | ||
151 | * Continuation called to notify client about result of the | ||
152 | * datastore removal operation. | ||
153 | * | ||
154 | * @param cls closure | ||
155 | * @param success #GNUNET_SYSERR on failure | ||
156 | * @param min_expiration minimum expiration time required for content to be stored | ||
157 | * @param msg NULL on success, otherwise an error message | ||
158 | */ | ||
159 | static void | ||
160 | process_cont (void *cls, | ||
161 | int success, | ||
162 | struct GNUNET_TIME_Absolute min_expiration, | ||
163 | const char *msg) | ||
164 | { | ||
165 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
166 | |||
167 | if (success == GNUNET_SYSERR) | ||
168 | { | ||
169 | uc->emsg = GNUNET_strdup (msg); | ||
170 | signal_unindex_error (uc); | ||
171 | return; | ||
172 | } | ||
173 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
174 | "Datastore REMOVE operation succeeded\n"); | ||
175 | GNUNET_FS_tree_encoder_next (uc->tc); | ||
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * Function called asking for the current (encoded) | ||
181 | * block to be processed. After processing the | ||
182 | * client should either call "GNUNET_FS_tree_encode_next" | ||
183 | * or (on error) "GNUNET_FS_tree_encode_finish". | ||
184 | * | ||
185 | * @param cls closure | ||
186 | * @param chk content hash key for the block (key for lookup in the datastore) | ||
187 | * @param offset offset of the block | ||
188 | * @param depth depth of the block, 0 for DBLOCK | ||
189 | * @param type type of the block (IBLOCK or DBLOCK) | ||
190 | * @param block the (encrypted) block | ||
191 | * @param block_size size of block (in bytes) | ||
192 | */ | ||
193 | static void | ||
194 | unindex_process (void *cls, | ||
195 | const struct ContentHashKey *chk, | ||
196 | uint64_t offset, | ||
197 | unsigned int depth, | ||
198 | enum GNUNET_BLOCK_Type type, | ||
199 | const void *block, | ||
200 | uint16_t block_size) | ||
201 | { | ||
202 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
203 | uint32_t size; | ||
204 | const void *data; | ||
205 | struct OnDemandBlock odb; | ||
206 | |||
207 | if (type != GNUNET_BLOCK_TYPE_FS_DBLOCK) | ||
208 | { | ||
209 | size = block_size; | ||
210 | data = block; | ||
211 | } | ||
212 | else /* on-demand encoded DBLOCK */ | ||
213 | { | ||
214 | size = sizeof(struct OnDemandBlock); | ||
215 | odb.offset = GNUNET_htonll (offset); | ||
216 | odb.file_id = uc->file_id; | ||
217 | data = &odb; | ||
218 | } | ||
219 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
220 | "Sending REMOVE request to DATASTORE service\n"); | ||
221 | GNUNET_DATASTORE_remove (uc->dsh, &chk->query, size, data, -2, 1, | ||
222 | &process_cont, uc); | ||
223 | uc->chk = *chk; | ||
224 | } | ||
225 | |||
226 | |||
227 | /** | ||
228 | * Function called with the response from the FS service to our | ||
229 | * unindexing request. | ||
230 | * | ||
231 | * @param cls closure, unindex context | ||
232 | * @param msg the response | ||
233 | */ | ||
234 | static void | ||
235 | handle_unindex_response (void *cls, | ||
236 | const struct GNUNET_MessageHeader *msg) | ||
237 | { | ||
238 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
239 | struct GNUNET_FS_ProgressInfo pi; | ||
240 | |||
241 | if (NULL != uc->mq) | ||
242 | { | ||
243 | GNUNET_MQ_destroy (uc->mq); | ||
244 | uc->mq = NULL; | ||
245 | } | ||
246 | uc->state = UNINDEX_STATE_COMPLETE; | ||
247 | pi.status = GNUNET_FS_STATUS_UNINDEX_COMPLETED; | ||
248 | pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO; | ||
249 | GNUNET_FS_unindex_sync_ (uc); | ||
250 | GNUNET_FS_unindex_make_status_ (&pi, | ||
251 | uc, | ||
252 | uc->file_size); | ||
253 | } | ||
254 | |||
255 | |||
256 | /** | ||
257 | * Generic error handler, called with the appropriate error code and | ||
258 | * the same closure specified at the creation of the message queue. | ||
259 | * Not every message queue implementation supports an error handler. | ||
260 | * | ||
261 | * @param cls closure with the `struct GNUNET_FS_UnindexContext *` | ||
262 | * @param error error code | ||
263 | */ | ||
264 | static void | ||
265 | unindex_mq_error_handler (void *cls, | ||
266 | enum GNUNET_MQ_Error error) | ||
267 | { | ||
268 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
269 | |||
270 | if (NULL != uc->mq) | ||
271 | { | ||
272 | GNUNET_MQ_destroy (uc->mq); | ||
273 | uc->mq = NULL; | ||
274 | } | ||
275 | uc->state = UNINDEX_STATE_ERROR; | ||
276 | uc->emsg = GNUNET_strdup (_ ("Error communicating with `fs' service.")); | ||
277 | GNUNET_FS_unindex_sync_ (uc); | ||
278 | signal_unindex_error (uc); | ||
279 | } | ||
280 | |||
281 | |||
282 | /** | ||
283 | * Function called when we are done with removing UBlocks. | ||
284 | * Disconnect from datastore and notify FS service about | ||
285 | * the unindex event. | ||
286 | * | ||
287 | * @param uc our unindexing context | ||
288 | */ | ||
289 | static void | ||
290 | unindex_finish (struct GNUNET_FS_UnindexContext *uc) | ||
291 | { | ||
292 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
293 | GNUNET_MQ_hd_fixed_size (unindex_response, | ||
294 | GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK, | ||
295 | struct GNUNET_MessageHeader, | ||
296 | uc), | ||
297 | GNUNET_MQ_handler_end () | ||
298 | }; | ||
299 | char *emsg; | ||
300 | struct GNUNET_MQ_Envelope *env; | ||
301 | struct UnindexMessage *req; | ||
302 | |||
303 | /* generate final progress message */ | ||
304 | unindex_progress (uc, | ||
305 | uc->file_size, | ||
306 | NULL, | ||
307 | 0, | ||
308 | 0); | ||
309 | GNUNET_FS_tree_encoder_finish (uc->tc, | ||
310 | &emsg); | ||
311 | uc->tc = NULL; | ||
312 | GNUNET_DISK_file_close (uc->fh); | ||
313 | uc->fh = NULL; | ||
314 | GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO); | ||
315 | uc->dsh = NULL; | ||
316 | uc->state = UNINDEX_STATE_FS_NOTIFY; | ||
317 | GNUNET_FS_unindex_sync_ (uc); | ||
318 | uc->mq = GNUNET_CLIENT_connect (uc->h->cfg, | ||
319 | "fs", | ||
320 | handlers, | ||
321 | &unindex_mq_error_handler, | ||
322 | uc); | ||
323 | if (NULL == uc->mq) | ||
324 | { | ||
325 | uc->state = UNINDEX_STATE_ERROR; | ||
326 | uc->emsg = | ||
327 | GNUNET_strdup (_ ("Failed to connect to FS service for unindexing.")); | ||
328 | GNUNET_FS_unindex_sync_ (uc); | ||
329 | signal_unindex_error (uc); | ||
330 | return; | ||
331 | } | ||
332 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
333 | "Sending UNINDEX message to FS service\n"); | ||
334 | env = GNUNET_MQ_msg (req, | ||
335 | GNUNET_MESSAGE_TYPE_FS_UNINDEX); | ||
336 | req->reserved = 0; | ||
337 | req->file_id = uc->file_id; | ||
338 | GNUNET_MQ_send (uc->mq, | ||
339 | env); | ||
340 | } | ||
341 | |||
342 | |||
343 | /** | ||
344 | * Function called by the directory scanner as we extract keywords | ||
345 | * that we will need to remove UBlocks. | ||
346 | * | ||
347 | * @param cls the 'struct GNUNET_FS_UnindexContext *' | ||
348 | * @param filename which file we are making progress on | ||
349 | * @param is_directory #GNUNET_YES if this is a directory, | ||
350 | * #GNUNET_NO if this is a file | ||
351 | * #GNUNET_SYSERR if it is neither (or unknown) | ||
352 | * @param reason kind of progress we are making | ||
353 | */ | ||
354 | static void | ||
355 | unindex_directory_scan_cb (void *cls, | ||
356 | const char *filename, | ||
357 | int is_directory, | ||
358 | enum GNUNET_FS_DirScannerProgressUpdateReason reason) | ||
359 | { | ||
360 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
361 | static struct GNUNET_FS_ShareTreeItem *directory_scan_result; | ||
362 | |||
363 | switch (reason) | ||
364 | { | ||
365 | case GNUNET_FS_DIRSCANNER_FINISHED: | ||
366 | directory_scan_result = GNUNET_FS_directory_scan_get_result (uc->dscan); | ||
367 | uc->dscan = NULL; | ||
368 | if (NULL != directory_scan_result->ksk_uri) | ||
369 | { | ||
370 | uc->ksk_uri = GNUNET_FS_uri_dup (directory_scan_result->ksk_uri); | ||
371 | uc->state = UNINDEX_STATE_DS_REMOVE_KBLOCKS; | ||
372 | GNUNET_FS_unindex_sync_ (uc); | ||
373 | GNUNET_FS_unindex_do_remove_kblocks_ (uc); | ||
374 | } | ||
375 | else | ||
376 | { | ||
377 | uc->emsg = GNUNET_strdup (_ ("Failed to get KSKs from directory scan.")); | ||
378 | GNUNET_FS_unindex_sync_ (uc); | ||
379 | unindex_finish (uc); | ||
380 | } | ||
381 | GNUNET_FS_share_tree_free (directory_scan_result); | ||
382 | break; | ||
383 | |||
384 | case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR: | ||
385 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
386 | _ ("Internal error scanning `%s'.\n"), | ||
387 | uc->filename); | ||
388 | GNUNET_FS_directory_scan_abort (uc->dscan); | ||
389 | uc->dscan = NULL; | ||
390 | uc->emsg = GNUNET_strdup (_ ("Failed to get KSKs from directory scan.")); | ||
391 | GNUNET_FS_unindex_sync_ (uc); | ||
392 | unindex_finish (uc); | ||
393 | break; | ||
394 | |||
395 | default: | ||
396 | break; | ||
397 | } | ||
398 | } | ||
399 | |||
400 | |||
401 | /** | ||
402 | * If necessary, connect to the datastore and remove the UBlocks. | ||
403 | * | ||
404 | * @param uc context for the unindex operation. | ||
405 | */ | ||
406 | void | ||
407 | GNUNET_FS_unindex_do_extract_keywords_ (struct GNUNET_FS_UnindexContext *uc) | ||
408 | { | ||
409 | char *ex; | ||
410 | |||
411 | if (GNUNET_OK != | ||
412 | GNUNET_CONFIGURATION_get_value_string (uc->h->cfg, "FS", "EXTRACTORS", | ||
413 | &ex)) | ||
414 | ex = NULL; | ||
415 | uc->dscan = GNUNET_FS_directory_scan_start (uc->filename, | ||
416 | GNUNET_NO, ex, | ||
417 | &unindex_directory_scan_cb, | ||
418 | uc); | ||
419 | GNUNET_free (ex); | ||
420 | } | ||
421 | |||
422 | |||
423 | /** | ||
424 | * Continuation called to notify client about result of the remove | ||
425 | * operation for the UBlock. | ||
426 | * | ||
427 | * @param cls the 'struct GNUNET_FS_UnindexContext *' | ||
428 | * @param success GNUNET_SYSERR on failure (including timeout/queue drop) | ||
429 | * GNUNET_NO if content was already there | ||
430 | * GNUNET_YES (or other positive value) on success | ||
431 | * @param min_expiration minimum expiration time required for 0-priority content to be stored | ||
432 | * by the datacache at this time, zero for unknown, forever if we have no | ||
433 | * space for 0-priority content | ||
434 | * @param msg NULL on success, otherwise an error message | ||
435 | */ | ||
436 | static void | ||
437 | continue_after_remove (void *cls, | ||
438 | int32_t success, | ||
439 | struct GNUNET_TIME_Absolute min_expiration, | ||
440 | const char *msg) | ||
441 | { | ||
442 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
443 | |||
444 | uc->dqe = NULL; | ||
445 | if (success != GNUNET_YES) | ||
446 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
447 | _ ("Failed to remove UBlock: %s\n"), | ||
448 | msg); | ||
449 | uc->ksk_offset++; | ||
450 | GNUNET_FS_unindex_do_remove_kblocks_ (uc); | ||
451 | } | ||
452 | |||
453 | |||
454 | /** | ||
455 | * Function called from datastore with result from us looking for | ||
456 | * a UBlock. There are four cases: | ||
457 | * 1) no result, means we move on to the next keyword | ||
458 | * 2) data hash is the same as an already seen data hash, means we move on to | ||
459 | * next keyword | ||
460 | * 3) UBlock for a different CHK, means we keep looking for more | ||
461 | * 4) UBlock is for our CHK, means we remove the block and then move | ||
462 | * on to the next keyword | ||
463 | * | ||
464 | * @param cls the 'struct GNUNET_FS_UnindexContext *' | ||
465 | * @param key key for the content | ||
466 | * @param size number of bytes in data | ||
467 | * @param data content stored | ||
468 | * @param type type of the content | ||
469 | * @param priority priority of the content | ||
470 | * @param anonymity anonymity-level for the content | ||
471 | * @param replication replication-level for the content | ||
472 | * @param expiration expiration time for the content | ||
473 | * @param uid unique identifier for the datum; | ||
474 | * maybe 0 if no unique identifier is available | ||
475 | */ | ||
476 | static void | ||
477 | process_kblock_for_unindex (void *cls, | ||
478 | const struct GNUNET_HashCode *key, | ||
479 | size_t size, | ||
480 | const void *data, | ||
481 | enum GNUNET_BLOCK_Type type, | ||
482 | uint32_t priority, | ||
483 | uint32_t anonymity, | ||
484 | uint32_t replication, | ||
485 | struct GNUNET_TIME_Absolute expiration, | ||
486 | uint64_t uid) | ||
487 | { | ||
488 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
489 | const struct UBlock *ub; | ||
490 | struct GNUNET_FS_Uri *chk_uri; | ||
491 | struct GNUNET_HashCode query; | ||
492 | |||
493 | uc->dqe = NULL; | ||
494 | if (NULL == data) | ||
495 | { | ||
496 | /* no result */ | ||
497 | uc->ksk_offset++; | ||
498 | GNUNET_FS_unindex_do_remove_kblocks_ (uc); | ||
499 | return; | ||
500 | } | ||
501 | GNUNET_assert (GNUNET_BLOCK_TYPE_FS_UBLOCK == type); | ||
502 | if (size < sizeof(struct UBlock)) | ||
503 | { | ||
504 | GNUNET_break (0); | ||
505 | goto get_next; | ||
506 | } | ||
507 | ub = data; | ||
508 | GNUNET_CRYPTO_hash (&ub->verification_key, | ||
509 | sizeof(ub->verification_key), | ||
510 | &query); | ||
511 | if (0 != memcmp (&query, | ||
512 | key, | ||
513 | sizeof(struct GNUNET_HashCode))) | ||
514 | { | ||
515 | /* result does not match our keyword, skip */ | ||
516 | goto get_next; | ||
517 | } | ||
518 | { | ||
519 | char pt[size - sizeof(struct UBlock)]; | ||
520 | struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub; | ||
521 | const char *keyword; | ||
522 | |||
523 | GNUNET_CRYPTO_ecdsa_key_get_public ( | ||
524 | GNUNET_CRYPTO_ecdsa_key_get_anonymous (), | ||
525 | &anon_pub); | ||
526 | keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1]; | ||
527 | GNUNET_FS_ublock_decrypt_ (&ub[1], size - sizeof(struct UBlock), | ||
528 | &anon_pub, | ||
529 | keyword, | ||
530 | pt); | ||
531 | if (NULL == memchr (&pt[1], 0, sizeof(pt) - 1)) | ||
532 | { | ||
533 | GNUNET_break_op (0); /* malformed UBlock */ | ||
534 | goto get_next; | ||
535 | } | ||
536 | chk_uri = GNUNET_FS_uri_parse (&pt[1], NULL); | ||
537 | if (NULL == chk_uri) | ||
538 | { | ||
539 | GNUNET_break_op (0); /* malformed UBlock */ | ||
540 | goto get_next; | ||
541 | } | ||
542 | } | ||
543 | if (0 != memcmp (&uc->chk, | ||
544 | &chk_uri->data.chk.chk, | ||
545 | sizeof(struct ContentHashKey))) | ||
546 | { | ||
547 | /* different CHK, ignore */ | ||
548 | GNUNET_FS_uri_destroy (chk_uri); | ||
549 | goto get_next; | ||
550 | } | ||
551 | GNUNET_FS_uri_destroy (chk_uri); | ||
552 | /* matches! */ | ||
553 | uc->dqe = GNUNET_DATASTORE_remove (uc->dsh, | ||
554 | key, | ||
555 | size, | ||
556 | data, | ||
557 | 0 /* priority */, | ||
558 | 1 /* queue size */, | ||
559 | &continue_after_remove, | ||
560 | uc); | ||
561 | return; | ||
562 | get_next: | ||
563 | uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh, | ||
564 | uid + 1 /* next_uid */, | ||
565 | false /* random */, | ||
566 | &uc->uquery, | ||
567 | GNUNET_BLOCK_TYPE_FS_UBLOCK, | ||
568 | 0 /* priority */, | ||
569 | 1 /* queue size */, | ||
570 | &process_kblock_for_unindex, | ||
571 | uc); | ||
572 | } | ||
573 | |||
574 | |||
575 | /** | ||
576 | * If necessary, connect to the datastore and remove the KBlocks. | ||
577 | * | ||
578 | * @param uc context for the unindex operation. | ||
579 | */ | ||
580 | void | ||
581 | GNUNET_FS_unindex_do_remove_kblocks_ (struct GNUNET_FS_UnindexContext *uc) | ||
582 | { | ||
583 | const char *keyword; | ||
584 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon; | ||
585 | struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub; | ||
586 | struct GNUNET_CRYPTO_EcdsaPublicKey dpub; | ||
587 | |||
588 | if (NULL == uc->dsh) | ||
589 | uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg); | ||
590 | if (NULL == uc->dsh) | ||
591 | { | ||
592 | uc->state = UNINDEX_STATE_ERROR; | ||
593 | uc->emsg = GNUNET_strdup (_ ("Failed to connect to `datastore' service.")); | ||
594 | GNUNET_FS_unindex_sync_ (uc); | ||
595 | signal_unindex_error (uc); | ||
596 | return; | ||
597 | } | ||
598 | if ((NULL == uc->ksk_uri) || | ||
599 | (uc->ksk_offset >= uc->ksk_uri->data.ksk.keywordCount)) | ||
600 | { | ||
601 | unindex_finish (uc); | ||
602 | return; | ||
603 | } | ||
604 | anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous (); | ||
605 | GNUNET_CRYPTO_ecdsa_key_get_public (anon, | ||
606 | &anon_pub); | ||
607 | keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1]; | ||
608 | GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub, | ||
609 | keyword, | ||
610 | "fs-ublock", | ||
611 | &dpub); | ||
612 | GNUNET_CRYPTO_hash (&dpub, | ||
613 | sizeof(dpub), | ||
614 | &uc->uquery); | ||
615 | uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh, | ||
616 | 0 /* next_uid */, | ||
617 | false /* random */, | ||
618 | &uc->uquery, | ||
619 | GNUNET_BLOCK_TYPE_FS_UBLOCK, | ||
620 | 0 /* priority */, | ||
621 | 1 /* queue size */, | ||
622 | &process_kblock_for_unindex, | ||
623 | uc); | ||
624 | } | ||
625 | |||
626 | |||
627 | /** | ||
628 | * Function called when the tree encoder has | ||
629 | * processed all blocks. Clean up. | ||
630 | * | ||
631 | * @param cls our unindexing context | ||
632 | */ | ||
633 | static void | ||
634 | unindex_extract_keywords (void *cls) | ||
635 | { | ||
636 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
637 | |||
638 | uc->state = UNINDEX_STATE_EXTRACT_KEYWORDS; | ||
639 | GNUNET_FS_unindex_sync_ (uc); | ||
640 | GNUNET_FS_unindex_do_extract_keywords_ (uc); | ||
641 | } | ||
642 | |||
643 | |||
644 | /** | ||
645 | * Connect to the datastore and remove the blocks. | ||
646 | * | ||
647 | * @param uc context for the unindex operation. | ||
648 | */ | ||
649 | void | ||
650 | GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc) | ||
651 | { | ||
652 | if (NULL == uc->dsh) | ||
653 | uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg); | ||
654 | if (NULL == uc->dsh) | ||
655 | { | ||
656 | uc->state = UNINDEX_STATE_ERROR; | ||
657 | uc->emsg = GNUNET_strdup (_ ("Failed to connect to `datastore' service.")); | ||
658 | GNUNET_FS_unindex_sync_ (uc); | ||
659 | signal_unindex_error (uc); | ||
660 | return; | ||
661 | } | ||
662 | uc->fh = | ||
663 | GNUNET_DISK_file_open (uc->filename, GNUNET_DISK_OPEN_READ, | ||
664 | GNUNET_DISK_PERM_NONE); | ||
665 | if (NULL == uc->fh) | ||
666 | { | ||
667 | GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO); | ||
668 | uc->dsh = NULL; | ||
669 | uc->state = UNINDEX_STATE_ERROR; | ||
670 | uc->emsg = GNUNET_strdup (_ ("Failed to open file for unindexing.")); | ||
671 | GNUNET_FS_unindex_sync_ (uc); | ||
672 | signal_unindex_error (uc); | ||
673 | return; | ||
674 | } | ||
675 | uc->tc = | ||
676 | GNUNET_FS_tree_encoder_create (uc->h, | ||
677 | uc->file_size, | ||
678 | uc, | ||
679 | &unindex_reader, | ||
680 | &unindex_process, | ||
681 | &unindex_progress, | ||
682 | &unindex_extract_keywords); | ||
683 | GNUNET_FS_tree_encoder_next (uc->tc); | ||
684 | } | ||
685 | |||
686 | |||
687 | /** | ||
688 | * Function called once the hash of the file | ||
689 | * that is being unindexed has been computed. | ||
690 | * | ||
691 | * @param cls closure, unindex context | ||
692 | * @param file_id computed hash, NULL on error | ||
693 | */ | ||
694 | void | ||
695 | GNUNET_FS_unindex_process_hash_ (void *cls, | ||
696 | const struct GNUNET_HashCode *file_id) | ||
697 | { | ||
698 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
699 | |||
700 | uc->fhc = NULL; | ||
701 | if (uc->state != UNINDEX_STATE_HASHING) | ||
702 | { | ||
703 | GNUNET_FS_unindex_stop (uc); | ||
704 | return; | ||
705 | } | ||
706 | if (file_id == NULL) | ||
707 | { | ||
708 | uc->state = UNINDEX_STATE_ERROR; | ||
709 | uc->emsg = GNUNET_strdup (_ ("Failed to compute hash of file.")); | ||
710 | GNUNET_FS_unindex_sync_ (uc); | ||
711 | signal_unindex_error (uc); | ||
712 | return; | ||
713 | } | ||
714 | uc->file_id = *file_id; | ||
715 | uc->state = UNINDEX_STATE_DS_REMOVE; | ||
716 | GNUNET_FS_unindex_sync_ (uc); | ||
717 | GNUNET_FS_unindex_do_remove_ (uc); | ||
718 | } | ||
719 | |||
720 | |||
721 | /** | ||
722 | * Create SUSPEND event for the given unindex operation | ||
723 | * and then clean up our state (without stop signal). | ||
724 | * | ||
725 | * @param cls the `struct GNUNET_FS_UnindexContext` to signal for | ||
726 | */ | ||
727 | void | ||
728 | GNUNET_FS_unindex_signal_suspend_ (void *cls) | ||
729 | { | ||
730 | struct GNUNET_FS_UnindexContext *uc = cls; | ||
731 | struct GNUNET_FS_ProgressInfo pi; | ||
732 | |||
733 | /* FIXME: lots of duplication with unindex_stop here! */ | ||
734 | if (uc->dscan != NULL) | ||
735 | { | ||
736 | GNUNET_FS_directory_scan_abort (uc->dscan); | ||
737 | uc->dscan = NULL; | ||
738 | } | ||
739 | if (NULL != uc->dqe) | ||
740 | { | ||
741 | GNUNET_DATASTORE_cancel (uc->dqe); | ||
742 | uc->dqe = NULL; | ||
743 | } | ||
744 | if (uc->fhc != NULL) | ||
745 | { | ||
746 | GNUNET_CRYPTO_hash_file_cancel (uc->fhc); | ||
747 | uc->fhc = NULL; | ||
748 | } | ||
749 | if (NULL != uc->ksk_uri) | ||
750 | { | ||
751 | GNUNET_FS_uri_destroy (uc->ksk_uri); | ||
752 | uc->ksk_uri = NULL; | ||
753 | } | ||
754 | if (NULL != uc->mq) | ||
755 | { | ||
756 | GNUNET_MQ_destroy (uc->mq); | ||
757 | uc->mq = NULL; | ||
758 | } | ||
759 | if (NULL != uc->dsh) | ||
760 | { | ||
761 | GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO); | ||
762 | uc->dsh = NULL; | ||
763 | } | ||
764 | if (NULL != uc->tc) | ||
765 | { | ||
766 | GNUNET_FS_tree_encoder_finish (uc->tc, NULL); | ||
767 | uc->tc = NULL; | ||
768 | } | ||
769 | if (uc->fh != NULL) | ||
770 | { | ||
771 | GNUNET_DISK_file_close (uc->fh); | ||
772 | uc->fh = NULL; | ||
773 | } | ||
774 | GNUNET_FS_end_top (uc->h, uc->top); | ||
775 | pi.status = GNUNET_FS_STATUS_UNINDEX_SUSPEND; | ||
776 | GNUNET_FS_unindex_make_status_ (&pi, uc, | ||
777 | (uc->state == | ||
778 | UNINDEX_STATE_COMPLETE) ? uc->file_size : 0); | ||
779 | GNUNET_break (NULL == uc->client_info); | ||
780 | GNUNET_free (uc->filename); | ||
781 | GNUNET_free (uc->serialization); | ||
782 | GNUNET_free (uc->emsg); | ||
783 | GNUNET_free (uc); | ||
784 | } | ||
785 | |||
786 | |||
787 | /** | ||
788 | * Unindex a file. | ||
789 | * | ||
790 | * @param h handle to the file sharing subsystem | ||
791 | * @param filename file to unindex | ||
792 | * @param cctx initial value for the client context | ||
793 | * @return NULL on error, otherwise handle | ||
794 | */ | ||
795 | struct GNUNET_FS_UnindexContext * | ||
796 | GNUNET_FS_unindex_start (struct GNUNET_FS_Handle *h, | ||
797 | const char *filename, | ||
798 | void *cctx) | ||
799 | { | ||
800 | struct GNUNET_FS_UnindexContext *uc; | ||
801 | struct GNUNET_FS_ProgressInfo pi; | ||
802 | uint64_t size; | ||
803 | |||
804 | if (GNUNET_OK != | ||
805 | GNUNET_DISK_file_size (filename, | ||
806 | &size, | ||
807 | GNUNET_YES, | ||
808 | GNUNET_YES)) | ||
809 | return NULL; | ||
810 | uc = GNUNET_new (struct GNUNET_FS_UnindexContext); | ||
811 | uc->h = h; | ||
812 | uc->filename = GNUNET_strdup (filename); | ||
813 | uc->start_time = GNUNET_TIME_absolute_get (); | ||
814 | uc->file_size = size; | ||
815 | uc->client_info = cctx; | ||
816 | GNUNET_FS_unindex_sync_ (uc); | ||
817 | pi.status = GNUNET_FS_STATUS_UNINDEX_START; | ||
818 | pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL; | ||
819 | GNUNET_FS_unindex_make_status_ (&pi, uc, 0); | ||
820 | uc->fhc = | ||
821 | GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
822 | filename, | ||
823 | HASHING_BLOCKSIZE, | ||
824 | &GNUNET_FS_unindex_process_hash_, uc); | ||
825 | uc->top = GNUNET_FS_make_top (h, | ||
826 | &GNUNET_FS_unindex_signal_suspend_, | ||
827 | uc); | ||
828 | return uc; | ||
829 | } | ||
830 | |||
831 | |||
832 | /** | ||
833 | * Clean up after completion of an unindex operation. | ||
834 | * | ||
835 | * @param uc handle | ||
836 | */ | ||
837 | void | ||
838 | GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc) | ||
839 | { | ||
840 | struct GNUNET_FS_ProgressInfo pi; | ||
841 | |||
842 | if (NULL != uc->dscan) | ||
843 | { | ||
844 | GNUNET_FS_directory_scan_abort (uc->dscan); | ||
845 | uc->dscan = NULL; | ||
846 | } | ||
847 | if (NULL != uc->dqe) | ||
848 | { | ||
849 | GNUNET_DATASTORE_cancel (uc->dqe); | ||
850 | uc->dqe = NULL; | ||
851 | } | ||
852 | if (NULL != uc->fhc) | ||
853 | { | ||
854 | GNUNET_CRYPTO_hash_file_cancel (uc->fhc); | ||
855 | uc->fhc = NULL; | ||
856 | } | ||
857 | if (NULL != uc->mq) | ||
858 | { | ||
859 | GNUNET_MQ_destroy (uc->mq); | ||
860 | uc->mq = NULL; | ||
861 | } | ||
862 | if (NULL != uc->dsh) | ||
863 | { | ||
864 | GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO); | ||
865 | uc->dsh = NULL; | ||
866 | } | ||
867 | if (NULL != uc->ksk_uri) | ||
868 | { | ||
869 | GNUNET_FS_uri_destroy (uc->ksk_uri); | ||
870 | uc->ksk_uri = NULL; | ||
871 | } | ||
872 | if (NULL != uc->tc) | ||
873 | { | ||
874 | GNUNET_FS_tree_encoder_finish (uc->tc, NULL); | ||
875 | uc->tc = NULL; | ||
876 | } | ||
877 | if (uc->fh != NULL) | ||
878 | { | ||
879 | GNUNET_DISK_file_close (uc->fh); | ||
880 | uc->fh = NULL; | ||
881 | } | ||
882 | GNUNET_FS_end_top (uc->h, uc->top); | ||
883 | if (uc->serialization != NULL) | ||
884 | { | ||
885 | GNUNET_FS_remove_sync_file_ (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, | ||
886 | uc->serialization); | ||
887 | GNUNET_free (uc->serialization); | ||
888 | uc->serialization = NULL; | ||
889 | } | ||
890 | pi.status = GNUNET_FS_STATUS_UNINDEX_STOPPED; | ||
891 | pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO; | ||
892 | GNUNET_FS_unindex_make_status_ (&pi, uc, | ||
893 | (uc->state == | ||
894 | UNINDEX_STATE_COMPLETE) ? uc->file_size : 0); | ||
895 | GNUNET_break (NULL == uc->client_info); | ||
896 | GNUNET_free (uc->emsg); | ||
897 | GNUNET_free (uc->filename); | ||
898 | GNUNET_free (uc); | ||
899 | } | ||
900 | |||
901 | |||
902 | /* end of fs_unindex.c */ | ||
diff --git a/src/fs/fs_uri.c b/src/fs/fs_uri.c deleted file mode 100644 index b0be0db4f..000000000 --- a/src/fs/fs_uri.c +++ /dev/null | |||
@@ -1,2045 +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 | |||
85 | #include "gnunet_fs_service.h" | ||
86 | #include "gnunet_signatures.h" | ||
87 | #include "fs_api.h" | ||
88 | #include <unitypes.h> | ||
89 | #include <unicase.h> | ||
90 | #include <uniconv.h> | ||
91 | #include <unistr.h> | ||
92 | #include <unistdio.h> | ||
93 | |||
94 | |||
95 | int | ||
96 | GNUNET_FS_uri_to_key (const struct GNUNET_FS_Uri *uri, | ||
97 | struct GNUNET_HashCode *key) | ||
98 | { | ||
99 | switch (uri->type) | ||
100 | { | ||
101 | case GNUNET_FS_URI_CHK: | ||
102 | *key = uri->data.chk.chk.query; | ||
103 | return GNUNET_OK; | ||
104 | |||
105 | case GNUNET_FS_URI_SKS: | ||
106 | GNUNET_CRYPTO_hash (uri->data.sks.identifier, | ||
107 | strlen (uri->data.sks.identifier), | ||
108 | key); | ||
109 | return GNUNET_OK; | ||
110 | |||
111 | case GNUNET_FS_URI_KSK: | ||
112 | if (uri->data.ksk.keywordCount > 0) | ||
113 | { | ||
114 | GNUNET_CRYPTO_hash (uri->data.ksk.keywords[0], | ||
115 | strlen (uri->data.ksk.keywords[0]), | ||
116 | key); | ||
117 | return GNUNET_OK; | ||
118 | } | ||
119 | else | ||
120 | { | ||
121 | memset (key, 0, sizeof(struct GNUNET_HashCode)); | ||
122 | return GNUNET_SYSERR; | ||
123 | } | ||
124 | break; | ||
125 | |||
126 | case GNUNET_FS_URI_LOC: | ||
127 | GNUNET_CRYPTO_hash (&uri->data.loc.fi, | ||
128 | sizeof(struct FileIdentifier) | ||
129 | + sizeof(struct GNUNET_PeerIdentity), | ||
130 | key); | ||
131 | return GNUNET_OK; | ||
132 | |||
133 | default: | ||
134 | memset (key, 0, sizeof(struct GNUNET_HashCode)); | ||
135 | return GNUNET_SYSERR; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | |||
140 | /** | ||
141 | * Convert keyword URI to a human readable format | ||
142 | * (i.e. the search query that was used in the first place) | ||
143 | * | ||
144 | * @param uri ksk uri to convert to a string | ||
145 | * @return string with the keywords | ||
146 | */ | ||
147 | char * | ||
148 | GNUNET_FS_uri_ksk_to_string_fancy (const struct GNUNET_FS_Uri *uri) | ||
149 | { | ||
150 | size_t n; | ||
151 | char *ret; | ||
152 | unsigned int i; | ||
153 | const char *keyword; | ||
154 | char **keywords; | ||
155 | unsigned int keywordCount; | ||
156 | |||
157 | if ((NULL == uri) || (GNUNET_FS_URI_KSK != uri->type)) | ||
158 | { | ||
159 | GNUNET_break (0); | ||
160 | return NULL; | ||
161 | } | ||
162 | keywords = uri->data.ksk.keywords; | ||
163 | keywordCount = uri->data.ksk.keywordCount; | ||
164 | n = keywordCount + 1; | ||
165 | for (i = 0; i < keywordCount; i++) | ||
166 | { | ||
167 | keyword = keywords[i]; | ||
168 | n += strlen (keyword) - 1; | ||
169 | if (NULL != strstr (&keyword[1], " ")) | ||
170 | n += 2; | ||
171 | if (keyword[0] == '+') | ||
172 | n++; | ||
173 | } | ||
174 | ret = GNUNET_malloc (n); | ||
175 | strcpy (ret, ""); | ||
176 | for (i = 0; i < keywordCount; i++) | ||
177 | { | ||
178 | keyword = keywords[i]; | ||
179 | if (NULL != strstr (&keyword[1], " ")) | ||
180 | { | ||
181 | strcat (ret, "\""); | ||
182 | if (keyword[0] == '+') | ||
183 | strcat (ret, keyword); | ||
184 | else | ||
185 | strcat (ret, &keyword[1]); | ||
186 | strcat (ret, "\""); | ||
187 | } | ||
188 | else | ||
189 | { | ||
190 | if (keyword[0] == '+') | ||
191 | strcat (ret, keyword); | ||
192 | else | ||
193 | strcat (ret, &keyword[1]); | ||
194 | } | ||
195 | strcat (ret, " "); | ||
196 | } | ||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | |||
201 | /** | ||
202 | * Given a keyword with %-encoding (and possibly quotes to protect | ||
203 | * spaces), return a copy of the keyword without %-encoding and | ||
204 | * without double-quotes (%22). Also, add a space at the beginning | ||
205 | * if there is not a '+'. | ||
206 | * | ||
207 | * @param in string with %-encoding | ||
208 | * @param emsg where to store the parser error message (if any) | ||
209 | * @return decoded string with leading space (or preserved plus) | ||
210 | */ | ||
211 | static char * | ||
212 | percent_decode_keyword (const char *in, char **emsg) | ||
213 | { | ||
214 | char *out; | ||
215 | char *ret; | ||
216 | unsigned int rpos; | ||
217 | unsigned int wpos; | ||
218 | unsigned int hx; | ||
219 | |||
220 | out = GNUNET_strdup (in); | ||
221 | rpos = 0; | ||
222 | wpos = 0; | ||
223 | while (out[rpos] != '\0') | ||
224 | { | ||
225 | if (out[rpos] == '%') | ||
226 | { | ||
227 | if (1 != sscanf (&out[rpos + 1], "%2X", &hx)) | ||
228 | { | ||
229 | GNUNET_free (out); | ||
230 | *emsg = GNUNET_strdup ( | ||
231 | _ ( /* xgettext:no-c-format */ | ||
232 | "Malformed KSK URI (`%' must be followed by HEX number)")); | ||
233 | return NULL; | ||
234 | } | ||
235 | rpos += 3; | ||
236 | if (hx == '"') | ||
237 | continue; /* skip double quote */ | ||
238 | out[wpos++] = (char) hx; | ||
239 | } | ||
240 | else | ||
241 | { | ||
242 | out[wpos++] = out[rpos++]; | ||
243 | } | ||
244 | } | ||
245 | out[wpos] = '\0'; | ||
246 | if (out[0] == '+') | ||
247 | { | ||
248 | ret = GNUNET_strdup (out); | ||
249 | } | ||
250 | else | ||
251 | { | ||
252 | /* need to prefix with space */ | ||
253 | ret = GNUNET_malloc (strlen (out) + 2); | ||
254 | strcpy (ret, " "); | ||
255 | strcat (ret, out); | ||
256 | } | ||
257 | GNUNET_free (out); | ||
258 | return ret; | ||
259 | } | ||
260 | |||
261 | |||
262 | #define GNUNET_FS_URI_KSK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_KSK_INFIX | ||
263 | |||
264 | /** | ||
265 | * Parse a KSK URI. | ||
266 | * | ||
267 | * @param s an uri string | ||
268 | * @param emsg where to store the parser error message (if any) | ||
269 | * @return NULL on error, otherwise the KSK URI | ||
270 | */ | ||
271 | static struct GNUNET_FS_Uri * | ||
272 | uri_ksk_parse (const char *s, char **emsg) | ||
273 | { | ||
274 | struct GNUNET_FS_Uri *ret; | ||
275 | char **keywords; | ||
276 | unsigned int pos; | ||
277 | int max; | ||
278 | int iret; | ||
279 | int i; | ||
280 | size_t slen; | ||
281 | char *dup; | ||
282 | int saw_quote; | ||
283 | |||
284 | slen = strlen (s); | ||
285 | pos = strlen (GNUNET_FS_URI_KSK_PREFIX); | ||
286 | if ((slen <= pos) || (0 != strncmp (s, GNUNET_FS_URI_KSK_PREFIX, pos))) | ||
287 | return NULL; /* not KSK URI */ | ||
288 | if ((s[slen - 1] == '+') || (s[pos] == '+')) | ||
289 | { | ||
290 | *emsg = | ||
291 | GNUNET_strdup (_ ("Malformed KSK URI (must not begin or end with `+')")); | ||
292 | return NULL; | ||
293 | } | ||
294 | max = 1; | ||
295 | saw_quote = 0; | ||
296 | for (i = pos; i < slen; i++) | ||
297 | { | ||
298 | if ((s[i] == '%') && (&s[i] == strstr (&s[i], "%22"))) | ||
299 | { | ||
300 | saw_quote = (saw_quote + 1) % 2; | ||
301 | i += 3; | ||
302 | continue; | ||
303 | } | ||
304 | if ((s[i] == '+') && (saw_quote == 0)) | ||
305 | { | ||
306 | max++; | ||
307 | if (s[i - 1] == '+') | ||
308 | { | ||
309 | *emsg = GNUNET_strdup (_ ("Malformed KSK URI (`++' not allowed)")); | ||
310 | return NULL; | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | if (saw_quote == 1) | ||
315 | { | ||
316 | *emsg = GNUNET_strdup (_ ("Malformed KSK URI (quotes not balanced)")); | ||
317 | return NULL; | ||
318 | } | ||
319 | iret = max; | ||
320 | dup = GNUNET_strdup (s); | ||
321 | keywords = GNUNET_new_array (max, char *); | ||
322 | for (i = slen - 1; i >= (int) pos; i--) | ||
323 | { | ||
324 | if ((s[i] == '%') && (&s[i] == strstr (&s[i], "%22"))) | ||
325 | { | ||
326 | saw_quote = (saw_quote + 1) % 2; | ||
327 | continue; | ||
328 | } | ||
329 | if ((dup[i] == '+') && (saw_quote == 0)) | ||
330 | { | ||
331 | keywords[--max] = percent_decode_keyword (&dup[i + 1], emsg); | ||
332 | if (NULL == keywords[max]) | ||
333 | goto CLEANUP; | ||
334 | dup[i] = '\0'; | ||
335 | } | ||
336 | } | ||
337 | keywords[--max] = percent_decode_keyword (&dup[pos], emsg); | ||
338 | if (NULL == keywords[max]) | ||
339 | goto CLEANUP; | ||
340 | GNUNET_assert (0 == max); | ||
341 | GNUNET_free (dup); | ||
342 | ret = GNUNET_new (struct GNUNET_FS_Uri); | ||
343 | ret->type = GNUNET_FS_URI_KSK; | ||
344 | ret->data.ksk.keywordCount = iret; | ||
345 | ret->data.ksk.keywords = keywords; | ||
346 | return ret; | ||
347 | CLEANUP: | ||
348 | for (i = 0; i < max; i++) | ||
349 | GNUNET_free (keywords[i]); | ||
350 | GNUNET_free (keywords); | ||
351 | GNUNET_free (dup); | ||
352 | return NULL; | ||
353 | } | ||
354 | |||
355 | |||
356 | #define GNUNET_FS_URI_SKS_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_SKS_INFIX | ||
357 | |||
358 | /** | ||
359 | * Parse an SKS URI. | ||
360 | * | ||
361 | * @param s an uri string | ||
362 | * @param emsg where to store the parser error message (if any) | ||
363 | * @return NULL on error, SKS URI otherwise | ||
364 | */ | ||
365 | static struct GNUNET_FS_Uri * | ||
366 | uri_sks_parse (const char *s, char **emsg) | ||
367 | { | ||
368 | struct GNUNET_FS_Uri *ret; | ||
369 | struct GNUNET_CRYPTO_EcdsaPublicKey ns; | ||
370 | size_t pos; | ||
371 | char *end; | ||
372 | |||
373 | pos = strlen (GNUNET_FS_URI_SKS_PREFIX); | ||
374 | if ((strlen (s) <= pos) || (0 != strncmp (s, GNUNET_FS_URI_SKS_PREFIX, pos))) | ||
375 | return NULL; /* not an SKS URI */ | ||
376 | end = strchr (&s[pos], '/'); | ||
377 | if ((NULL == end) || | ||
378 | (GNUNET_OK != GNUNET_STRINGS_string_to_data (&s[pos], | ||
379 | end - &s[pos], | ||
380 | &ns, | ||
381 | sizeof(ns)))) | ||
382 | { | ||
383 | *emsg = GNUNET_strdup (_ ("Malformed SKS URI (wrong syntax)")); | ||
384 | return NULL; /* malformed */ | ||
385 | } | ||
386 | end++; /* skip over '/' */ | ||
387 | ret = GNUNET_new (struct GNUNET_FS_Uri); | ||
388 | ret->type = GNUNET_FS_URI_SKS; | ||
389 | ret->data.sks.ns = ns; | ||
390 | ret->data.sks.identifier = GNUNET_strdup (end); | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | |||
395 | #define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX | ||
396 | |||
397 | |||
398 | /** | ||
399 | * Parse a CHK URI. | ||
400 | * | ||
401 | * @param s an uri string | ||
402 | * @param emsg where to store the parser error message (if any) | ||
403 | * @return NULL on error, CHK URI otherwise | ||
404 | */ | ||
405 | static struct GNUNET_FS_Uri * | ||
406 | uri_chk_parse (const char *s, char **emsg) | ||
407 | { | ||
408 | struct GNUNET_FS_Uri *ret; | ||
409 | struct FileIdentifier fi; | ||
410 | unsigned int pos; | ||
411 | unsigned long long flen; | ||
412 | size_t slen; | ||
413 | char h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)]; | ||
414 | char h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)]; | ||
415 | |||
416 | slen = strlen (s); | ||
417 | pos = strlen (GNUNET_FS_URI_CHK_PREFIX); | ||
418 | if ((slen < pos + 2 * sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) + 1) || | ||
419 | (0 != strncmp (s, GNUNET_FS_URI_CHK_PREFIX, pos))) | ||
420 | return NULL; /* not a CHK URI */ | ||
421 | if ((s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] != '.') || | ||
422 | (s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2 - 1] != '.')) | ||
423 | { | ||
424 | *emsg = GNUNET_strdup (_ ("Malformed CHK URI (wrong syntax)")); | ||
425 | return NULL; | ||
426 | } | ||
427 | GNUNET_memcpy (h1, &s[pos], sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)); | ||
428 | h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0'; | ||
429 | GNUNET_memcpy (h2, | ||
430 | &s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)], | ||
431 | sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)); | ||
432 | h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0'; | ||
433 | |||
434 | if ((GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h1, &fi.chk.key)) || | ||
435 | (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h2, &fi.chk.query)) || | ||
436 | (1 != | ||
437 | sscanf (&s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2], | ||
438 | "%llu", | ||
439 | &flen))) | ||
440 | { | ||
441 | *emsg = GNUNET_strdup (_ ("Malformed CHK URI (failed to decode CHK)")); | ||
442 | return NULL; | ||
443 | } | ||
444 | fi.file_length = GNUNET_htonll (flen); | ||
445 | ret = GNUNET_new (struct GNUNET_FS_Uri); | ||
446 | ret->type = GNUNET_FS_URI_CHK; | ||
447 | ret->data.chk = fi; | ||
448 | return ret; | ||
449 | } | ||
450 | |||
451 | |||
452 | GNUNET_NETWORK_STRUCT_BEGIN | ||
453 | /** | ||
454 | * Structure that defines how the contents of a location URI must be | ||
455 | * assembled in memory to create or verify the signature of a location | ||
456 | * URI. | ||
457 | */ | ||
458 | struct LocUriAssembly | ||
459 | { | ||
460 | /** | ||
461 | * What is being signed (rest of this struct). | ||
462 | */ | ||
463 | struct GNUNET_CRYPTO_EccSignaturePurpose purpose; | ||
464 | |||
465 | /** | ||
466 | * Expiration time of the offer. | ||
467 | */ | ||
468 | struct GNUNET_TIME_AbsoluteNBO exptime; | ||
469 | |||
470 | /** | ||
471 | * File being offered. | ||
472 | */ | ||
473 | struct FileIdentifier fi; | ||
474 | |||
475 | /** | ||
476 | * Peer offering the file. | ||
477 | */ | ||
478 | struct GNUNET_PeerIdentity peer; | ||
479 | }; | ||
480 | GNUNET_NETWORK_STRUCT_END | ||
481 | |||
482 | |||
483 | #define GNUNET_FS_URI_LOC_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_LOC_INFIX | ||
484 | |||
485 | #define SIGNATURE_ASCII_LENGTH 103 | ||
486 | |||
487 | /** | ||
488 | * Parse a LOC URI. | ||
489 | * Also verifies validity of the location URI. | ||
490 | * | ||
491 | * @param s an uri string | ||
492 | * @param emsg where to store the parser error message (if any) | ||
493 | * @return NULL on error, valid LOC URI otherwise | ||
494 | */ | ||
495 | static struct GNUNET_FS_Uri * | ||
496 | uri_loc_parse (const char *s, char **emsg) | ||
497 | { | ||
498 | struct GNUNET_FS_Uri *uri; | ||
499 | char h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)]; | ||
500 | char h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)]; | ||
501 | unsigned int pos; | ||
502 | unsigned int npos; | ||
503 | unsigned long long exptime; | ||
504 | unsigned long long flen; | ||
505 | struct GNUNET_TIME_Absolute et; | ||
506 | struct GNUNET_CRYPTO_EddsaSignature sig; | ||
507 | struct LocUriAssembly ass; | ||
508 | size_t slen; | ||
509 | |||
510 | slen = strlen (s); | ||
511 | pos = strlen (GNUNET_FS_URI_LOC_PREFIX); | ||
512 | if ((slen < pos + 2 * sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) + 1) || | ||
513 | (0 != strncmp (s, GNUNET_FS_URI_LOC_PREFIX, pos))) | ||
514 | return NULL; /* not a LOC URI */ | ||
515 | if ((s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] != '.') || | ||
516 | (s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2 - 1] != '.')) | ||
517 | { | ||
518 | *emsg = GNUNET_strdup (_ ("LOC URI malformed (wrong syntax)")); | ||
519 | return NULL; | ||
520 | } | ||
521 | GNUNET_memcpy (h1, &s[pos], sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)); | ||
522 | h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0'; | ||
523 | GNUNET_memcpy (h2, | ||
524 | &s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)], | ||
525 | sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)); | ||
526 | h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0'; | ||
527 | |||
528 | if ((GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h1, &ass.fi.chk.key)) || | ||
529 | (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h2, &ass.fi.chk.query)) || | ||
530 | (1 != | ||
531 | sscanf (&s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2], | ||
532 | "%llu", | ||
533 | &flen))) | ||
534 | { | ||
535 | *emsg = GNUNET_strdup (_ ("LOC URI malformed (no CHK)")); | ||
536 | return NULL; | ||
537 | } | ||
538 | ass.fi.file_length = GNUNET_htonll (flen); | ||
539 | |||
540 | npos = pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2; | ||
541 | while ((s[npos] != '\0') && (s[npos] != '.')) | ||
542 | npos++; | ||
543 | if (s[npos] == '\0') | ||
544 | { | ||
545 | *emsg = GNUNET_strdup (_ ("LOC URI malformed (missing LOC)")); | ||
546 | goto ERR; | ||
547 | } | ||
548 | npos++; | ||
549 | if ((strlen (&s[npos]) <= GNUNET_CRYPTO_PKEY_ASCII_LENGTH + 1) || | ||
550 | ('.' != s[npos + GNUNET_CRYPTO_PKEY_ASCII_LENGTH])) | ||
551 | { | ||
552 | *emsg = | ||
553 | GNUNET_strdup (_ ("LOC URI malformed (wrong syntax for public key)")); | ||
554 | } | ||
555 | if ( | ||
556 | GNUNET_OK != | ||
557 | GNUNET_CRYPTO_eddsa_public_key_from_string (&s[npos], | ||
558 | GNUNET_CRYPTO_PKEY_ASCII_LENGTH, | ||
559 | &ass.peer.public_key)) | ||
560 | { | ||
561 | *emsg = | ||
562 | GNUNET_strdup (_ ("LOC URI malformed (could not decode public key)")); | ||
563 | goto ERR; | ||
564 | } | ||
565 | npos += GNUNET_CRYPTO_PKEY_ASCII_LENGTH; | ||
566 | if (s[npos++] != '.') | ||
567 | { | ||
568 | *emsg = GNUNET_strdup (_ ("LOC URI malformed (could not find signature)")); | ||
569 | goto ERR; | ||
570 | } | ||
571 | if ((strlen (&s[npos]) <= SIGNATURE_ASCII_LENGTH + 1) || | ||
572 | ('.' != s[npos + SIGNATURE_ASCII_LENGTH])) | ||
573 | { | ||
574 | *emsg = | ||
575 | GNUNET_strdup (_ ("LOC URI malformed (wrong syntax for signature)")); | ||
576 | goto ERR; | ||
577 | } | ||
578 | if (GNUNET_OK != | ||
579 | GNUNET_STRINGS_string_to_data (&s[npos], | ||
580 | SIGNATURE_ASCII_LENGTH, | ||
581 | &sig, | ||
582 | sizeof( | ||
583 | struct GNUNET_CRYPTO_EddsaSignature))) | ||
584 | { | ||
585 | *emsg = | ||
586 | GNUNET_strdup (_ ("LOC URI malformed (could not decode signature)")); | ||
587 | goto ERR; | ||
588 | } | ||
589 | npos += SIGNATURE_ASCII_LENGTH; | ||
590 | if (s[npos++] != '.') | ||
591 | { | ||
592 | *emsg = GNUNET_strdup ( | ||
593 | _ ("LOC URI malformed (wrong syntax for expiration time)")); | ||
594 | goto ERR; | ||
595 | } | ||
596 | if (1 != sscanf (&s[npos], "%llu", &exptime)) | ||
597 | { | ||
598 | *emsg = | ||
599 | GNUNET_strdup (_ ("LOC URI malformed (could not parse expiration time)")); | ||
600 | goto ERR; | ||
601 | } | ||
602 | ass.purpose.size = htonl (sizeof(struct LocUriAssembly)); | ||
603 | ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT); | ||
604 | et.abs_value_us = exptime * 1000LL * 1000LL; | ||
605 | ass.exptime = GNUNET_TIME_absolute_hton (et); | ||
606 | if (GNUNET_OK != | ||
607 | GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT, | ||
608 | &ass, | ||
609 | &sig, | ||
610 | &ass.peer.public_key)) | ||
611 | { | ||
612 | *emsg = | ||
613 | GNUNET_strdup (_ ("LOC URI malformed (signature failed validation)")); | ||
614 | goto ERR; | ||
615 | } | ||
616 | uri = GNUNET_new (struct GNUNET_FS_Uri); | ||
617 | uri->type = GNUNET_FS_URI_LOC; | ||
618 | uri->data.loc.fi = ass.fi; | ||
619 | uri->data.loc.peer = ass.peer; | ||
620 | uri->data.loc.expirationTime = et; | ||
621 | uri->data.loc.contentSignature = sig; | ||
622 | |||
623 | return uri; | ||
624 | ERR: | ||
625 | return NULL; | ||
626 | } | ||
627 | |||
628 | |||
629 | /** | ||
630 | * Convert a UTF-8 String to a URI. | ||
631 | * | ||
632 | * @param uri string to parse | ||
633 | * @param emsg where to store the parser error message (if any) | ||
634 | * @return NULL on error | ||
635 | */ | ||
636 | struct GNUNET_FS_Uri * | ||
637 | GNUNET_FS_uri_parse (const char *uri, char **emsg) | ||
638 | { | ||
639 | struct GNUNET_FS_Uri *ret; | ||
640 | char *msg; | ||
641 | |||
642 | if (NULL == uri) | ||
643 | { | ||
644 | GNUNET_break (0); | ||
645 | if (NULL != emsg) | ||
646 | *emsg = GNUNET_strdup (_ ("invalid argument")); | ||
647 | return NULL; | ||
648 | } | ||
649 | /** | ||
650 | * FIXME: Do we want to log this? | ||
651 | */ | ||
652 | msg = NULL; | ||
653 | if (NULL != (ret = uri_chk_parse (uri, &msg))) | ||
654 | return ret; | ||
655 | GNUNET_free (msg); | ||
656 | if (NULL != (ret = uri_ksk_parse (uri, &msg))) | ||
657 | return ret; | ||
658 | GNUNET_free (msg); | ||
659 | if (NULL != (ret = uri_sks_parse (uri, &msg))) | ||
660 | return ret; | ||
661 | GNUNET_free (msg); | ||
662 | if (NULL != (ret = uri_loc_parse (uri, &msg))) | ||
663 | return ret; | ||
664 | GNUNET_free (msg); | ||
665 | if (NULL != emsg) | ||
666 | *emsg = GNUNET_strdup (_ ("Unrecognized URI type")); | ||
667 | return NULL; | ||
668 | } | ||
669 | |||
670 | |||
671 | /** | ||
672 | * Free URI. | ||
673 | * | ||
674 | * @param uri uri to free | ||
675 | */ | ||
676 | void | ||
677 | GNUNET_FS_uri_destroy (struct GNUNET_FS_Uri *uri) | ||
678 | { | ||
679 | unsigned int i; | ||
680 | |||
681 | switch (uri->type) | ||
682 | { | ||
683 | case GNUNET_FS_URI_KSK: | ||
684 | for (i = 0; i < uri->data.ksk.keywordCount; i++) | ||
685 | GNUNET_free (uri->data.ksk.keywords[i]); | ||
686 | GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount, 0); | ||
687 | break; | ||
688 | |||
689 | case GNUNET_FS_URI_SKS: | ||
690 | GNUNET_free (uri->data.sks.identifier); | ||
691 | break; | ||
692 | |||
693 | case GNUNET_FS_URI_LOC: | ||
694 | break; | ||
695 | |||
696 | default: | ||
697 | /* do nothing */ | ||
698 | break; | ||
699 | } | ||
700 | GNUNET_free (uri); | ||
701 | } | ||
702 | |||
703 | |||
704 | /** | ||
705 | * How many keywords are ANDed in this keyword URI? | ||
706 | * | ||
707 | * @param uri ksk uri to get the number of keywords from | ||
708 | * @return 0 if this is not a keyword URI | ||
709 | */ | ||
710 | unsigned int | ||
711 | GNUNET_FS_uri_ksk_get_keyword_count (const struct GNUNET_FS_Uri *uri) | ||
712 | { | ||
713 | if (uri->type != GNUNET_FS_URI_KSK) | ||
714 | return 0; | ||
715 | return uri->data.ksk.keywordCount; | ||
716 | } | ||
717 | |||
718 | |||
719 | int | ||
720 | GNUNET_FS_uri_ksk_get_keywords (const struct GNUNET_FS_Uri *uri, | ||
721 | GNUNET_FS_KeywordIterator iterator, | ||
722 | void *iterator_cls) | ||
723 | { | ||
724 | unsigned int i; | ||
725 | char *keyword; | ||
726 | |||
727 | if (uri->type != GNUNET_FS_URI_KSK) | ||
728 | return -1; | ||
729 | if (NULL == iterator) | ||
730 | return uri->data.ksk.keywordCount; | ||
731 | for (i = 0; i < uri->data.ksk.keywordCount; i++) | ||
732 | { | ||
733 | keyword = uri->data.ksk.keywords[i]; | ||
734 | /* first character of keyword indicates | ||
735 | * if it is mandatory or not */ | ||
736 | if (GNUNET_OK != iterator (iterator_cls, &keyword[1],(keyword[0] == '+') )) | ||
737 | return i; | ||
738 | } | ||
739 | return i; | ||
740 | } | ||
741 | |||
742 | |||
743 | /** | ||
744 | * Add the given keyword to the set of keywords represented by the URI. | ||
745 | * Does nothing if the keyword is already present. | ||
746 | * | ||
747 | * @param uri ksk uri to modify | ||
748 | * @param keyword keyword to add | ||
749 | * @param is_mandatory is this keyword mandatory? | ||
750 | */ | ||
751 | void | ||
752 | GNUNET_FS_uri_ksk_add_keyword (struct GNUNET_FS_Uri *uri, | ||
753 | const char *keyword, | ||
754 | int is_mandatory) | ||
755 | { | ||
756 | unsigned int i; | ||
757 | const char *old; | ||
758 | char *n; | ||
759 | |||
760 | GNUNET_assert (uri->type == GNUNET_FS_URI_KSK); | ||
761 | for (i = 0; i < uri->data.ksk.keywordCount; i++) | ||
762 | { | ||
763 | old = uri->data.ksk.keywords[i]; | ||
764 | if (0 == strcmp (&old[1], keyword)) | ||
765 | return; | ||
766 | } | ||
767 | GNUNET_asprintf (&n, is_mandatory ? "+%s" : " %s", keyword); | ||
768 | GNUNET_array_append (uri->data.ksk.keywords, uri->data.ksk.keywordCount, n); | ||
769 | } | ||
770 | |||
771 | |||
772 | /** | ||
773 | * Remove the given keyword from the set of keywords represented by the URI. | ||
774 | * Does nothing if the keyword is not present. | ||
775 | * | ||
776 | * @param uri ksk uri to modify | ||
777 | * @param keyword keyword to add | ||
778 | */ | ||
779 | void | ||
780 | GNUNET_FS_uri_ksk_remove_keyword (struct GNUNET_FS_Uri *uri, | ||
781 | const char *keyword) | ||
782 | { | ||
783 | unsigned int i; | ||
784 | char *old; | ||
785 | |||
786 | GNUNET_assert (uri->type == GNUNET_FS_URI_KSK); | ||
787 | for (i = 0; i < uri->data.ksk.keywordCount; i++) | ||
788 | { | ||
789 | old = uri->data.ksk.keywords[i]; | ||
790 | if (0 == strcmp (&old[1], keyword)) | ||
791 | { | ||
792 | uri->data.ksk.keywords[i] = | ||
793 | uri->data.ksk.keywords[uri->data.ksk.keywordCount - 1]; | ||
794 | GNUNET_array_grow (uri->data.ksk.keywords, | ||
795 | uri->data.ksk.keywordCount, | ||
796 | uri->data.ksk.keywordCount - 1); | ||
797 | GNUNET_free (old); | ||
798 | return; | ||
799 | } | ||
800 | } | ||
801 | } | ||
802 | |||
803 | |||
804 | /** | ||
805 | * Obtain the identity of the peer offering the data | ||
806 | * | ||
807 | * @param uri the location URI to inspect | ||
808 | * @param peer where to store the identify of the peer (presumably) offering the content | ||
809 | * @return #GNUNET_SYSERR if this is not a location URI, otherwise #GNUNET_OK | ||
810 | */ | ||
811 | int | ||
812 | GNUNET_FS_uri_loc_get_peer_identity (const struct GNUNET_FS_Uri *uri, | ||
813 | struct GNUNET_PeerIdentity *peer) | ||
814 | { | ||
815 | if (uri->type != GNUNET_FS_URI_LOC) | ||
816 | return GNUNET_SYSERR; | ||
817 | *peer = uri->data.loc.peer; | ||
818 | return GNUNET_OK; | ||
819 | } | ||
820 | |||
821 | |||
822 | /** | ||
823 | * Obtain the expiration of the LOC URI. | ||
824 | * | ||
825 | * @param uri location URI to get the expiration from | ||
826 | * @return expiration time of the URI | ||
827 | */ | ||
828 | struct GNUNET_TIME_Absolute | ||
829 | GNUNET_FS_uri_loc_get_expiration (const struct GNUNET_FS_Uri *uri) | ||
830 | { | ||
831 | GNUNET_assert (uri->type == GNUNET_FS_URI_LOC); | ||
832 | return uri->data.loc.expirationTime; | ||
833 | } | ||
834 | |||
835 | |||
836 | /** | ||
837 | * Obtain the URI of the content itself. | ||
838 | * | ||
839 | * @param uri location URI to get the content URI from | ||
840 | * @return NULL if argument is not a location URI | ||
841 | */ | ||
842 | struct GNUNET_FS_Uri * | ||
843 | GNUNET_FS_uri_loc_get_uri (const struct GNUNET_FS_Uri *uri) | ||
844 | { | ||
845 | struct GNUNET_FS_Uri *ret; | ||
846 | |||
847 | if (uri->type != GNUNET_FS_URI_LOC) | ||
848 | return NULL; | ||
849 | ret = GNUNET_new (struct GNUNET_FS_Uri); | ||
850 | ret->type = GNUNET_FS_URI_CHK; | ||
851 | ret->data.chk = uri->data.loc.fi; | ||
852 | return ret; | ||
853 | } | ||
854 | |||
855 | |||
856 | /** | ||
857 | * Construct a location URI (this peer will be used for the location). | ||
858 | * This function should only be called from within gnunet-service-fs, | ||
859 | * as it requires the peer's private key which is generally unavailable | ||
860 | * to processes directly under the user's control. However, for | ||
861 | * testing and as it logically fits under URIs, it is in this API. | ||
862 | * | ||
863 | * @param base_uri content offered by the sender | ||
864 | * @param sign_key private key of the peer | ||
865 | * @param expiration_time how long will the content be offered? | ||
866 | * @return the location URI, NULL on error | ||
867 | */ | ||
868 | struct GNUNET_FS_Uri * | ||
869 | GNUNET_FS_uri_loc_create (const struct GNUNET_FS_Uri *base_uri, | ||
870 | const struct GNUNET_CRYPTO_EddsaPrivateKey *sign_key, | ||
871 | struct GNUNET_TIME_Absolute expiration_time) | ||
872 | { | ||
873 | struct GNUNET_FS_Uri *uri; | ||
874 | struct GNUNET_CRYPTO_EddsaPublicKey my_public_key; | ||
875 | struct LocUriAssembly ass; | ||
876 | struct GNUNET_TIME_Absolute et; | ||
877 | |||
878 | if (GNUNET_FS_URI_CHK != base_uri->type) | ||
879 | return NULL; | ||
880 | /* we round expiration time to full seconds for SKS URIs */ | ||
881 | et.abs_value_us = (expiration_time.abs_value_us / 1000000LL) * 1000000LL; | ||
882 | GNUNET_CRYPTO_eddsa_key_get_public (sign_key, &my_public_key); | ||
883 | ass.purpose.size = htonl (sizeof(struct LocUriAssembly)); | ||
884 | ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT); | ||
885 | ass.exptime = GNUNET_TIME_absolute_hton (et); | ||
886 | ass.fi = base_uri->data.chk; | ||
887 | ass.peer.public_key = my_public_key; | ||
888 | uri = GNUNET_new (struct GNUNET_FS_Uri); | ||
889 | uri->type = GNUNET_FS_URI_LOC; | ||
890 | uri->data.loc.fi = base_uri->data.chk; | ||
891 | uri->data.loc.expirationTime = et; | ||
892 | uri->data.loc.peer.public_key = my_public_key; | ||
893 | GNUNET_CRYPTO_eddsa_sign (sign_key, | ||
894 | &ass, | ||
895 | &uri->data.loc.contentSignature); | ||
896 | return uri; | ||
897 | } | ||
898 | |||
899 | |||
900 | /** | ||
901 | * Create an SKS URI from a namespace ID and an identifier. | ||
902 | * | ||
903 | * @param ns namespace ID | ||
904 | * @param id identifier | ||
905 | * @return an FS URI for the given namespace and identifier | ||
906 | */ | ||
907 | struct GNUNET_FS_Uri * | ||
908 | GNUNET_FS_uri_sks_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *ns, | ||
909 | const char *id) | ||
910 | { | ||
911 | struct GNUNET_FS_Uri *ns_uri; | ||
912 | |||
913 | ns_uri = GNUNET_new (struct GNUNET_FS_Uri); | ||
914 | ns_uri->type = GNUNET_FS_URI_SKS; | ||
915 | ns_uri->data.sks.ns = *ns; | ||
916 | ns_uri->data.sks.identifier = GNUNET_strdup (id); | ||
917 | return ns_uri; | ||
918 | } | ||
919 | |||
920 | |||
921 | /** | ||
922 | * Merge the sets of keywords from two KSK URIs. | ||
923 | * (useful for merging the canonicalized keywords with | ||
924 | * the original keywords for sharing). | ||
925 | * | ||
926 | * @param u1 first uri | ||
927 | * @param u2 second uri | ||
928 | * @return merged URI, NULL on error | ||
929 | */ | ||
930 | struct GNUNET_FS_Uri * | ||
931 | GNUNET_FS_uri_ksk_merge (const struct GNUNET_FS_Uri *u1, | ||
932 | const struct GNUNET_FS_Uri *u2) | ||
933 | { | ||
934 | struct GNUNET_FS_Uri *ret; | ||
935 | unsigned int kc; | ||
936 | unsigned int i; | ||
937 | unsigned int j; | ||
938 | int found; | ||
939 | const char *kp; | ||
940 | char **kl; | ||
941 | |||
942 | if ((u1 == NULL) && (u2 == NULL)) | ||
943 | return NULL; | ||
944 | if (u1 == NULL) | ||
945 | return GNUNET_FS_uri_dup (u2); | ||
946 | if (u2 == NULL) | ||
947 | return GNUNET_FS_uri_dup (u1); | ||
948 | if ((u1->type != GNUNET_FS_URI_KSK) || (u2->type != GNUNET_FS_URI_KSK)) | ||
949 | { | ||
950 | GNUNET_break (0); | ||
951 | return NULL; | ||
952 | } | ||
953 | kc = u1->data.ksk.keywordCount; | ||
954 | kl = GNUNET_new_array (kc + u2->data.ksk.keywordCount, char *); | ||
955 | for (i = 0; i < u1->data.ksk.keywordCount; i++) | ||
956 | kl[i] = GNUNET_strdup (u1->data.ksk.keywords[i]); | ||
957 | for (i = 0; i < u2->data.ksk.keywordCount; i++) | ||
958 | { | ||
959 | kp = u2->data.ksk.keywords[i]; | ||
960 | found = 0; | ||
961 | for (j = 0; j < u1->data.ksk.keywordCount; j++) | ||
962 | if (0 == strcmp (kp + 1, kl[j] + 1)) | ||
963 | { | ||
964 | found = 1; | ||
965 | if (kp[0] == '+') | ||
966 | kl[j][0] = '+'; | ||
967 | break; | ||
968 | } | ||
969 | if (0 == found) | ||
970 | kl[kc++] = GNUNET_strdup (kp); | ||
971 | } | ||
972 | ret = GNUNET_new (struct GNUNET_FS_Uri); | ||
973 | ret->type = GNUNET_FS_URI_KSK; | ||
974 | ret->data.ksk.keywordCount = kc; | ||
975 | ret->data.ksk.keywords = kl; | ||
976 | return ret; | ||
977 | } | ||
978 | |||
979 | |||
980 | /** | ||
981 | * Duplicate URI. | ||
982 | * | ||
983 | * @param uri the URI to duplicate | ||
984 | * @return copy of the URI | ||
985 | */ | ||
986 | struct GNUNET_FS_Uri * | ||
987 | GNUNET_FS_uri_dup (const struct GNUNET_FS_Uri *uri) | ||
988 | { | ||
989 | struct GNUNET_FS_Uri *ret; | ||
990 | unsigned int i; | ||
991 | |||
992 | if (uri == NULL) | ||
993 | return NULL; | ||
994 | ret = GNUNET_new (struct GNUNET_FS_Uri); | ||
995 | GNUNET_memcpy (ret, uri, sizeof(struct GNUNET_FS_Uri)); | ||
996 | switch (ret->type) | ||
997 | { | ||
998 | case GNUNET_FS_URI_KSK: | ||
999 | if (ret->data.ksk.keywordCount >= | ||
1000 | GNUNET_MAX_MALLOC_CHECKED / sizeof(char *)) | ||
1001 | { | ||
1002 | GNUNET_break (0); | ||
1003 | GNUNET_free (ret); | ||
1004 | return NULL; | ||
1005 | } | ||
1006 | if (ret->data.ksk.keywordCount > 0) | ||
1007 | { | ||
1008 | ret->data.ksk.keywords = | ||
1009 | GNUNET_new_array (ret->data.ksk.keywordCount, char *); | ||
1010 | for (i = 0; i < ret->data.ksk.keywordCount; i++) | ||
1011 | ret->data.ksk.keywords[i] = GNUNET_strdup (uri->data.ksk.keywords[i]); | ||
1012 | } | ||
1013 | else | ||
1014 | ret->data.ksk.keywords = NULL; /* just to be sure */ | ||
1015 | break; | ||
1016 | |||
1017 | case GNUNET_FS_URI_SKS: | ||
1018 | ret->data.sks.identifier = GNUNET_strdup (uri->data.sks.identifier); | ||
1019 | break; | ||
1020 | |||
1021 | case GNUNET_FS_URI_LOC: | ||
1022 | break; | ||
1023 | |||
1024 | default: | ||
1025 | break; | ||
1026 | } | ||
1027 | return ret; | ||
1028 | } | ||
1029 | |||
1030 | |||
1031 | /** | ||
1032 | * Create an FS URI from a single user-supplied string of keywords. | ||
1033 | * The string is broken up at spaces into individual keywords. | ||
1034 | * Keywords that start with "+" are mandatory. Double-quotes can | ||
1035 | * be used to prevent breaking up strings at spaces (and also | ||
1036 | * to specify non-mandatory keywords starting with "+"). | ||
1037 | * | ||
1038 | * Keywords must contain a balanced number of double quotes and | ||
1039 | * double quotes can not be used in the actual keywords (for | ||
1040 | * example, the string '""foo bar""' will be turned into two | ||
1041 | * "OR"ed keywords 'foo' and 'bar', not into '"foo bar"'. | ||
1042 | * | ||
1043 | * @param keywords the keyword string | ||
1044 | * @param emsg where to store an error message | ||
1045 | * @return an FS URI for the given keywords, NULL | ||
1046 | * if keywords is not legal (i.e. empty). | ||
1047 | */ | ||
1048 | struct GNUNET_FS_Uri * | ||
1049 | GNUNET_FS_uri_ksk_create (const char *keywords, char **emsg) | ||
1050 | { | ||
1051 | char **keywordarr; | ||
1052 | unsigned int num_Words; | ||
1053 | int inWord; | ||
1054 | char *pos; | ||
1055 | struct GNUNET_FS_Uri *uri; | ||
1056 | char *searchString; | ||
1057 | int saw_quote; | ||
1058 | |||
1059 | if (keywords == NULL) | ||
1060 | { | ||
1061 | *emsg = GNUNET_strdup (_ ("No keywords specified!\n")); | ||
1062 | GNUNET_break (0); | ||
1063 | return NULL; | ||
1064 | } | ||
1065 | searchString = GNUNET_strdup (keywords); | ||
1066 | num_Words = 0; | ||
1067 | inWord = 0; | ||
1068 | saw_quote = 0; | ||
1069 | pos = searchString; | ||
1070 | while ('\0' != *pos) | ||
1071 | { | ||
1072 | if ((saw_quote == 0) && (isspace ((unsigned char) *pos))) | ||
1073 | { | ||
1074 | inWord = 0; | ||
1075 | } | ||
1076 | else if (0 == inWord) | ||
1077 | { | ||
1078 | inWord = 1; | ||
1079 | ++num_Words; | ||
1080 | } | ||
1081 | if ('"' == *pos) | ||
1082 | saw_quote = (saw_quote + 1) % 2; | ||
1083 | pos++; | ||
1084 | } | ||
1085 | if (num_Words == 0) | ||
1086 | { | ||
1087 | GNUNET_free (searchString); | ||
1088 | *emsg = GNUNET_strdup (_ ("No keywords specified!\n")); | ||
1089 | return NULL; | ||
1090 | } | ||
1091 | if (saw_quote != 0) | ||
1092 | { | ||
1093 | GNUNET_free (searchString); | ||
1094 | *emsg = GNUNET_strdup (_ ("Number of double-quotes not balanced!\n")); | ||
1095 | return NULL; | ||
1096 | } | ||
1097 | keywordarr = GNUNET_new_array (num_Words, char *); | ||
1098 | num_Words = 0; | ||
1099 | inWord = 0; | ||
1100 | pos = searchString; | ||
1101 | while ('\0' != *pos) | ||
1102 | { | ||
1103 | if ((saw_quote == 0) && (isspace ((unsigned char) *pos))) | ||
1104 | { | ||
1105 | inWord = 0; | ||
1106 | *pos = '\0'; | ||
1107 | } | ||
1108 | else if (0 == inWord) | ||
1109 | { | ||
1110 | keywordarr[num_Words] = pos; | ||
1111 | inWord = 1; | ||
1112 | ++num_Words; | ||
1113 | } | ||
1114 | if ('"' == *pos) | ||
1115 | saw_quote = (saw_quote + 1) % 2; | ||
1116 | pos++; | ||
1117 | } | ||
1118 | uri = | ||
1119 | GNUNET_FS_uri_ksk_create_from_args (num_Words, (const char **) keywordarr); | ||
1120 | GNUNET_free (keywordarr); | ||
1121 | GNUNET_free (searchString); | ||
1122 | return uri; | ||
1123 | } | ||
1124 | |||
1125 | |||
1126 | /** | ||
1127 | * Create an FS URI from a user-supplied command line of keywords. | ||
1128 | * Arguments should start with "+" to indicate mandatory | ||
1129 | * keywords. | ||
1130 | * | ||
1131 | * @param argc number of keywords | ||
1132 | * @param argv keywords (double quotes are not required for | ||
1133 | * keywords containing spaces; however, double | ||
1134 | * quotes are required for keywords starting with | ||
1135 | * "+"); there is no mechanism for having double | ||
1136 | * quotes in the actual keywords (if the user | ||
1137 | * did specifically specify double quotes, the | ||
1138 | * caller should convert each double quote | ||
1139 | * into two single quotes). | ||
1140 | * @return an FS URI for the given keywords, NULL | ||
1141 | * if keywords is not legal (i.e. empty). | ||
1142 | */ | ||
1143 | struct GNUNET_FS_Uri * | ||
1144 | GNUNET_FS_uri_ksk_create_from_args (unsigned int argc, const char **argv) | ||
1145 | { | ||
1146 | unsigned int i; | ||
1147 | struct GNUNET_FS_Uri *uri; | ||
1148 | const char *keyword; | ||
1149 | char *val; | ||
1150 | const char *r; | ||
1151 | char *w; | ||
1152 | char *emsg; | ||
1153 | |||
1154 | if (argc == 0) | ||
1155 | return NULL; | ||
1156 | /* allow URI to be given as one and only keyword and | ||
1157 | * handle accordingly */ | ||
1158 | emsg = NULL; | ||
1159 | if ((argc == 1) && (strlen (argv[0]) > strlen (GNUNET_FS_URI_PREFIX)) && | ||
1160 | (0 == strncmp (argv[0], | ||
1161 | GNUNET_FS_URI_PREFIX, | ||
1162 | strlen (GNUNET_FS_URI_PREFIX))) && | ||
1163 | (NULL != (uri = GNUNET_FS_uri_parse (argv[0], &emsg)))) | ||
1164 | return uri; | ||
1165 | GNUNET_free (emsg); | ||
1166 | uri = GNUNET_new (struct GNUNET_FS_Uri); | ||
1167 | uri->type = GNUNET_FS_URI_KSK; | ||
1168 | uri->data.ksk.keywordCount = argc; | ||
1169 | uri->data.ksk.keywords = GNUNET_new_array (argc, char *); | ||
1170 | for (i = 0; i < argc; i++) | ||
1171 | { | ||
1172 | keyword = argv[i]; | ||
1173 | if (keyword[0] == '+') | ||
1174 | val = GNUNET_strdup (keyword); | ||
1175 | else | ||
1176 | GNUNET_asprintf (&val, " %s", keyword); | ||
1177 | r = val; | ||
1178 | w = val; | ||
1179 | while ('\0' != *r) | ||
1180 | { | ||
1181 | if ('"' == *r) | ||
1182 | r++; | ||
1183 | else | ||
1184 | *(w++) = *(r++); | ||
1185 | } | ||
1186 | *w = '\0'; | ||
1187 | uri->data.ksk.keywords[i] = val; | ||
1188 | } | ||
1189 | return uri; | ||
1190 | } | ||
1191 | |||
1192 | |||
1193 | /** | ||
1194 | * Test if two URIs are equal. | ||
1195 | * | ||
1196 | * @param u1 one of the URIs | ||
1197 | * @param u2 the other URI | ||
1198 | * @return #GNUNET_YES if the URIs are equal | ||
1199 | */ | ||
1200 | int | ||
1201 | GNUNET_FS_uri_test_equal (const struct GNUNET_FS_Uri *u1, | ||
1202 | const struct GNUNET_FS_Uri *u2) | ||
1203 | { | ||
1204 | int ret; | ||
1205 | unsigned int i; | ||
1206 | unsigned int j; | ||
1207 | |||
1208 | GNUNET_assert (u1 != NULL); | ||
1209 | GNUNET_assert (u2 != NULL); | ||
1210 | if (u1->type != u2->type) | ||
1211 | return GNUNET_NO; | ||
1212 | switch (u1->type) | ||
1213 | { | ||
1214 | case GNUNET_FS_URI_CHK: | ||
1215 | if (0 == | ||
1216 | memcmp (&u1->data.chk, &u2->data.chk, sizeof(struct FileIdentifier))) | ||
1217 | return GNUNET_YES; | ||
1218 | return GNUNET_NO; | ||
1219 | |||
1220 | case GNUNET_FS_URI_SKS: | ||
1221 | if ((0 == memcmp (&u1->data.sks.ns, | ||
1222 | &u2->data.sks.ns, | ||
1223 | sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey))) && | ||
1224 | (0 == strcmp (u1->data.sks.identifier, u2->data.sks.identifier))) | ||
1225 | |||
1226 | return GNUNET_YES; | ||
1227 | return GNUNET_NO; | ||
1228 | |||
1229 | case GNUNET_FS_URI_KSK: | ||
1230 | if (u1->data.ksk.keywordCount != u2->data.ksk.keywordCount) | ||
1231 | return GNUNET_NO; | ||
1232 | for (i = 0; i < u1->data.ksk.keywordCount; i++) | ||
1233 | { | ||
1234 | ret = GNUNET_NO; | ||
1235 | for (j = 0; j < u2->data.ksk.keywordCount; j++) | ||
1236 | { | ||
1237 | if (0 == strcmp (u1->data.ksk.keywords[i], u2->data.ksk.keywords[j])) | ||
1238 | { | ||
1239 | ret = GNUNET_YES; | ||
1240 | break; | ||
1241 | } | ||
1242 | } | ||
1243 | if (ret == GNUNET_NO) | ||
1244 | return GNUNET_NO; | ||
1245 | } | ||
1246 | return GNUNET_YES; | ||
1247 | |||
1248 | case GNUNET_FS_URI_LOC: | ||
1249 | if (memcmp (&u1->data.loc, | ||
1250 | &u2->data.loc, | ||
1251 | sizeof(struct FileIdentifier) | ||
1252 | + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) | ||
1253 | + sizeof(struct GNUNET_TIME_Absolute) | ||
1254 | + sizeof(unsigned short) + sizeof(unsigned short)) != 0) | ||
1255 | return GNUNET_NO; | ||
1256 | return GNUNET_YES; | ||
1257 | |||
1258 | default: | ||
1259 | return GNUNET_NO; | ||
1260 | } | ||
1261 | } | ||
1262 | |||
1263 | |||
1264 | /** | ||
1265 | * Is this a namespace URI? | ||
1266 | * | ||
1267 | * @param uri the uri to check | ||
1268 | * @return #GNUNET_YES if this is an SKS uri | ||
1269 | */ | ||
1270 | int | ||
1271 | GNUNET_FS_uri_test_sks (const struct GNUNET_FS_Uri *uri) | ||
1272 | { | ||
1273 | return uri->type == GNUNET_FS_URI_SKS; | ||
1274 | } | ||
1275 | |||
1276 | |||
1277 | /** | ||
1278 | * Get the ID of a namespace from the given | ||
1279 | * namespace URI. | ||
1280 | * | ||
1281 | * @param uri the uri to get the namespace ID from | ||
1282 | * @param pseudonym where to store the ID of the namespace | ||
1283 | * @return #GNUNET_OK on success | ||
1284 | */ | ||
1285 | int | ||
1286 | GNUNET_FS_uri_sks_get_namespace (const struct GNUNET_FS_Uri *uri, | ||
1287 | struct GNUNET_CRYPTO_EcdsaPublicKey *pseudonym) | ||
1288 | { | ||
1289 | if (! GNUNET_FS_uri_test_sks (uri)) | ||
1290 | { | ||
1291 | GNUNET_break (0); | ||
1292 | return GNUNET_SYSERR; | ||
1293 | } | ||
1294 | *pseudonym = uri->data.sks.ns; | ||
1295 | return GNUNET_OK; | ||
1296 | } | ||
1297 | |||
1298 | |||
1299 | /** | ||
1300 | * Get the content identifier of an SKS URI. | ||
1301 | * | ||
1302 | * @param uri the sks uri | ||
1303 | * @return NULL on error (not a valid SKS URI) | ||
1304 | */ | ||
1305 | char * | ||
1306 | GNUNET_FS_uri_sks_get_content_id (const struct GNUNET_FS_Uri *uri) | ||
1307 | { | ||
1308 | if (! GNUNET_FS_uri_test_sks (uri)) | ||
1309 | { | ||
1310 | GNUNET_break (0); | ||
1311 | return NULL; | ||
1312 | } | ||
1313 | return GNUNET_strdup (uri->data.sks.identifier); | ||
1314 | } | ||
1315 | |||
1316 | |||
1317 | /** | ||
1318 | * Is this a keyword URI? | ||
1319 | * | ||
1320 | * @param uri the uri | ||
1321 | * @return #GNUNET_YES if this is a KSK uri | ||
1322 | */ | ||
1323 | int | ||
1324 | GNUNET_FS_uri_test_ksk (const struct GNUNET_FS_Uri *uri) | ||
1325 | { | ||
1326 | #if EXTRA_CHECKS | ||
1327 | unsigned int i; | ||
1328 | |||
1329 | if (uri->type == GNUNET_FS_URI_KSK) | ||
1330 | { | ||
1331 | for (i = 0; i < uri->data.ksk.keywordCount; i++) | ||
1332 | GNUNET_assert (uri->data.ksk.keywords[i] != NULL); | ||
1333 | } | ||
1334 | #endif | ||
1335 | return uri->type == GNUNET_FS_URI_KSK; | ||
1336 | } | ||
1337 | |||
1338 | |||
1339 | /** | ||
1340 | * Is this a file (or directory) URI? | ||
1341 | * | ||
1342 | * @param uri the uri to check | ||
1343 | * @return #GNUNET_YES if this is a CHK uri | ||
1344 | */ | ||
1345 | int | ||
1346 | GNUNET_FS_uri_test_chk (const struct GNUNET_FS_Uri *uri) | ||
1347 | { | ||
1348 | return uri->type == GNUNET_FS_URI_CHK; | ||
1349 | } | ||
1350 | |||
1351 | |||
1352 | /** | ||
1353 | * What is the size of the file that this URI | ||
1354 | * refers to? | ||
1355 | * | ||
1356 | * @param uri the CHK URI to inspect | ||
1357 | * @return size of the file as specified in the CHK URI | ||
1358 | */ | ||
1359 | uint64_t | ||
1360 | GNUNET_FS_uri_chk_get_file_size (const struct GNUNET_FS_Uri *uri) | ||
1361 | { | ||
1362 | switch (uri->type) | ||
1363 | { | ||
1364 | case GNUNET_FS_URI_CHK: | ||
1365 | return GNUNET_ntohll (uri->data.chk.file_length); | ||
1366 | |||
1367 | case GNUNET_FS_URI_LOC: | ||
1368 | return GNUNET_ntohll (uri->data.loc.fi.file_length); | ||
1369 | |||
1370 | default: | ||
1371 | GNUNET_assert (0); | ||
1372 | } | ||
1373 | return 0; /* unreachable */ | ||
1374 | } | ||
1375 | |||
1376 | |||
1377 | /** | ||
1378 | * Is this a location URI? | ||
1379 | * | ||
1380 | * @param uri the uri to check | ||
1381 | * @return #GNUNET_YES if this is a LOC uri | ||
1382 | */ | ||
1383 | int | ||
1384 | GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri) | ||
1385 | { | ||
1386 | return uri->type == GNUNET_FS_URI_LOC; | ||
1387 | } | ||
1388 | |||
1389 | |||
1390 | /** | ||
1391 | * Add a keyword as non-mandatory (with ' '-prefix) to the | ||
1392 | * given keyword list at offset 'index'. The array is | ||
1393 | * guaranteed to be long enough. | ||
1394 | * | ||
1395 | * @param s keyword to add | ||
1396 | * @param array array to add the keyword to | ||
1397 | * @param index offset where to add the keyword | ||
1398 | */ | ||
1399 | static void | ||
1400 | insert_non_mandatory_keyword (const char *s, char **array, int index) | ||
1401 | { | ||
1402 | char *nkword; | ||
1403 | |||
1404 | GNUNET_asprintf (&nkword, | ||
1405 | " %s", /* space to mark as 'non mandatory' */ | ||
1406 | s); | ||
1407 | array[index] = nkword; | ||
1408 | } | ||
1409 | |||
1410 | |||
1411 | /** | ||
1412 | * Test if the given keyword @a s is already present in the | ||
1413 | * given array, ignoring the '+'-mandatory prefix in the array. | ||
1414 | * | ||
1415 | * @param s keyword to test | ||
1416 | * @param array keywords to test against, with ' ' or '+' prefix to ignore | ||
1417 | * @param array_length length of the @a array | ||
1418 | * @return #GNUNET_YES if the keyword exists, #GNUNET_NO if not | ||
1419 | */ | ||
1420 | static int | ||
1421 | find_duplicate (const char *s, const char **array, int array_length) | ||
1422 | { | ||
1423 | int j; | ||
1424 | |||
1425 | for (j = array_length - 1; j >= 0; j--) | ||
1426 | if (0 == strcmp (&array[j][1], s)) | ||
1427 | return GNUNET_YES; | ||
1428 | return GNUNET_NO; | ||
1429 | } | ||
1430 | |||
1431 | |||
1432 | /** | ||
1433 | * FIXME: comment | ||
1434 | */ | ||
1435 | static char * | ||
1436 | normalize_metadata (enum EXTRACTOR_MetaFormat format, | ||
1437 | const char *data, | ||
1438 | size_t data_len) | ||
1439 | { | ||
1440 | uint8_t *free_str = NULL; | ||
1441 | uint8_t *str_to_normalize = (uint8_t *) data; | ||
1442 | uint8_t *normalized; | ||
1443 | size_t r_len; | ||
1444 | |||
1445 | if (str_to_normalize == NULL) | ||
1446 | return NULL; | ||
1447 | /* Don't trust libextractor */ | ||
1448 | if (format == EXTRACTOR_METAFORMAT_UTF8) | ||
1449 | { | ||
1450 | free_str = (uint8_t *) u8_check ((const uint8_t *) data, data_len); | ||
1451 | if (free_str == NULL) | ||
1452 | free_str = NULL; | ||
1453 | else | ||
1454 | format = EXTRACTOR_METAFORMAT_C_STRING; | ||
1455 | } | ||
1456 | if (format == EXTRACTOR_METAFORMAT_C_STRING) | ||
1457 | { | ||
1458 | free_str = u8_strconv_from_encoding (data, | ||
1459 | locale_charset (), | ||
1460 | iconveh_escape_sequence); | ||
1461 | if (free_str == NULL) | ||
1462 | return NULL; | ||
1463 | } | ||
1464 | |||
1465 | normalized = u8_tolower (str_to_normalize, | ||
1466 | strlen ((char *) str_to_normalize), | ||
1467 | NULL, | ||
1468 | UNINORM_NFD, | ||
1469 | NULL, | ||
1470 | &r_len); | ||
1471 | /* free_str is allocated by libunistring internally, use free() */ | ||
1472 | if (free_str != NULL) | ||
1473 | free (free_str); | ||
1474 | if (normalized != NULL) | ||
1475 | { | ||
1476 | /* u8_tolower allocates a non-NULL-terminated string! */ | ||
1477 | free_str = GNUNET_malloc (r_len + 1); | ||
1478 | GNUNET_memcpy (free_str, normalized, r_len); | ||
1479 | free_str[r_len] = '\0'; | ||
1480 | free (normalized); | ||
1481 | normalized = free_str; | ||
1482 | } | ||
1483 | return (char *) normalized; | ||
1484 | } | ||
1485 | |||
1486 | |||
1487 | /** | ||
1488 | * Counts the number of UTF-8 characters (not bytes) in the string, | ||
1489 | * returns that count. | ||
1490 | */ | ||
1491 | static size_t | ||
1492 | u8_strcount (const uint8_t *s) | ||
1493 | { | ||
1494 | size_t count; | ||
1495 | ucs4_t c; | ||
1496 | |||
1497 | GNUNET_assert (s != NULL); | ||
1498 | if (s[0] == 0) | ||
1499 | return 0; | ||
1500 | for (count = 0; s != NULL; count++) | ||
1501 | s = u8_next (&c, s); | ||
1502 | return count - 1; | ||
1503 | } | ||
1504 | |||
1505 | |||
1506 | /** | ||
1507 | * Break the filename up by matching [], () and {} pairs to make | ||
1508 | * keywords. In case of nesting parentheses only the inner pair counts. | ||
1509 | * You can't escape parentheses to scan something like "[blah\{foo]" to | ||
1510 | * make a "blah{foo" keyword, this function is only a heuristic! | ||
1511 | * | ||
1512 | * @param s string to break down. | ||
1513 | * @param array array to fill with enclosed tokens. If NULL, then tokens | ||
1514 | * are only counted. | ||
1515 | * @param index index at which to start filling the array (entries prior | ||
1516 | * to it are used to check for duplicates). ignored if @a array == NULL. | ||
1517 | * @return number of tokens counted (including duplicates), or number of | ||
1518 | * tokens extracted (excluding duplicates). 0 if there are no | ||
1519 | * matching parens in the string (when counting), or when all tokens | ||
1520 | * were duplicates (when extracting). | ||
1521 | */ | ||
1522 | static int | ||
1523 | get_keywords_from_parens (const char *s, char **array, int index) | ||
1524 | { | ||
1525 | int count = 0; | ||
1526 | char *open_paren; | ||
1527 | char *close_paren; | ||
1528 | char *ss; | ||
1529 | char tmp; | ||
1530 | |||
1531 | if (NULL == s) | ||
1532 | return 0; | ||
1533 | ss = GNUNET_strdup (s); | ||
1534 | open_paren = ss - 1; | ||
1535 | while (NULL != (open_paren = strpbrk (open_paren + 1, "[{("))) | ||
1536 | { | ||
1537 | int match = 0; | ||
1538 | |||
1539 | close_paren = strpbrk (open_paren + 1, "]})"); | ||
1540 | if (NULL == close_paren) | ||
1541 | continue; | ||
1542 | switch (open_paren[0]) | ||
1543 | { | ||
1544 | case '[': | ||
1545 | if (']' == close_paren[0]) | ||
1546 | match = 1; | ||
1547 | break; | ||
1548 | |||
1549 | case '{': | ||
1550 | if ('}' == close_paren[0]) | ||
1551 | match = 1; | ||
1552 | break; | ||
1553 | |||
1554 | case '(': | ||
1555 | if (')' == close_paren[0]) | ||
1556 | match = 1; | ||
1557 | break; | ||
1558 | |||
1559 | default: | ||
1560 | break; | ||
1561 | } | ||
1562 | if (match && (close_paren - open_paren > 1)) | ||
1563 | { | ||
1564 | tmp = close_paren[0]; | ||
1565 | close_paren[0] = '\0'; | ||
1566 | /* Keywords must be at least 3 characters long */ | ||
1567 | if (u8_strcount ((const uint8_t *) &open_paren[1]) <= 2) | ||
1568 | { | ||
1569 | close_paren[0] = tmp; | ||
1570 | continue; | ||
1571 | } | ||
1572 | if (NULL != array) | ||
1573 | { | ||
1574 | char *normalized; | ||
1575 | if (GNUNET_NO == find_duplicate ((const char *) &open_paren[1], | ||
1576 | (const char **) array, | ||
1577 | index + count)) | ||
1578 | { | ||
1579 | insert_non_mandatory_keyword ((const char *) &open_paren[1], | ||
1580 | array, | ||
1581 | index + count); | ||
1582 | count++; | ||
1583 | } | ||
1584 | normalized = normalize_metadata (EXTRACTOR_METAFORMAT_UTF8, | ||
1585 | &open_paren[1], | ||
1586 | close_paren - &open_paren[1]); | ||
1587 | if (normalized != NULL) | ||
1588 | { | ||
1589 | if (GNUNET_NO == find_duplicate ((const char *) normalized, | ||
1590 | (const char **) array, | ||
1591 | index + count)) | ||
1592 | { | ||
1593 | insert_non_mandatory_keyword ((const char *) normalized, | ||
1594 | array, | ||
1595 | index + count); | ||
1596 | count++; | ||
1597 | } | ||
1598 | GNUNET_free (normalized); | ||
1599 | } | ||
1600 | } | ||
1601 | else | ||
1602 | count++; | ||
1603 | close_paren[0] = tmp; | ||
1604 | } | ||
1605 | } | ||
1606 | GNUNET_free (ss); | ||
1607 | return count; | ||
1608 | } | ||
1609 | |||
1610 | |||
1611 | /** | ||
1612 | * Where to break up keywords | ||
1613 | */ | ||
1614 | #define TOKENS "_. /-!?#&+@\"\'\\;:,()[]{}$<>|" | ||
1615 | |||
1616 | /** | ||
1617 | * Break the filename up by TOKENS to make | ||
1618 | * keywords. | ||
1619 | * | ||
1620 | * @param s string to break down. | ||
1621 | * @param array array to fill with tokens. If NULL, then tokens are only | ||
1622 | * counted. | ||
1623 | * @param index index at which to start filling the array (entries prior | ||
1624 | * to it are used to check for duplicates). ignored if @a array == NULL. | ||
1625 | * @return number of tokens (>1) counted (including duplicates), or number of | ||
1626 | * tokens extracted (excluding duplicates). 0 if there are no | ||
1627 | * separators in the string (when counting), or when all tokens were | ||
1628 | * duplicates (when extracting). | ||
1629 | */ | ||
1630 | static int | ||
1631 | get_keywords_from_tokens (const char *s, char **array, int index) | ||
1632 | { | ||
1633 | char *p; | ||
1634 | char *ss; | ||
1635 | int seps = 0; | ||
1636 | |||
1637 | ss = GNUNET_strdup (s); | ||
1638 | for (p = strtok (ss, TOKENS); p != NULL; p = strtok (NULL, TOKENS)) | ||
1639 | { | ||
1640 | /* Keywords must be at least 3 characters long */ | ||
1641 | if (u8_strcount ((const uint8_t *) p) <= 2) | ||
1642 | continue; | ||
1643 | if (NULL != array) | ||
1644 | { | ||
1645 | char *normalized; | ||
1646 | if (GNUNET_NO == find_duplicate (p, (const char **) array, index + seps)) | ||
1647 | { | ||
1648 | insert_non_mandatory_keyword (p, array, index + seps); | ||
1649 | seps++; | ||
1650 | } | ||
1651 | normalized = | ||
1652 | normalize_metadata (EXTRACTOR_METAFORMAT_UTF8, p, strlen (p)); | ||
1653 | if (normalized != NULL) | ||
1654 | { | ||
1655 | if (GNUNET_NO == find_duplicate ((const char *) normalized, | ||
1656 | (const char **) array, | ||
1657 | index + seps)) | ||
1658 | { | ||
1659 | insert_non_mandatory_keyword ((const char *) normalized, | ||
1660 | array, | ||
1661 | index + seps); | ||
1662 | seps++; | ||
1663 | } | ||
1664 | GNUNET_free (normalized); | ||
1665 | } | ||
1666 | } | ||
1667 | else | ||
1668 | seps++; | ||
1669 | } | ||
1670 | GNUNET_free (ss); | ||
1671 | return seps; | ||
1672 | } | ||
1673 | |||
1674 | |||
1675 | #undef TOKENS | ||
1676 | |||
1677 | |||
1678 | /** | ||
1679 | * Function called on each value in the meta data. | ||
1680 | * Adds it to the URI. | ||
1681 | * | ||
1682 | * @param cls URI to update | ||
1683 | * @param plugin_name name of the plugin that produced this value; | ||
1684 | * special values can be used (e.g. '<zlib>' for zlib being | ||
1685 | * used in the main libextractor library and yielding | ||
1686 | * meta data). | ||
1687 | * @param type libextractor-type describing the meta data | ||
1688 | * @param format basic format information about data | ||
1689 | * @param data_mime_type mime-type of data (not of the original file); | ||
1690 | * can be NULL (if mime-type is not known) | ||
1691 | * @param data actual meta-data found | ||
1692 | * @param data_len number of bytes in @a data | ||
1693 | * @return 0 (always) | ||
1694 | */ | ||
1695 | static int | ||
1696 | gather_uri_data (void *cls, | ||
1697 | const char *plugin_name, | ||
1698 | enum EXTRACTOR_MetaType type, | ||
1699 | enum EXTRACTOR_MetaFormat format, | ||
1700 | const char *data_mime_type, | ||
1701 | const char *data, | ||
1702 | size_t data_len) | ||
1703 | { | ||
1704 | struct GNUNET_FS_Uri *uri = cls; | ||
1705 | char *normalized_data; | ||
1706 | const char *sep; | ||
1707 | |||
1708 | if ((format != EXTRACTOR_METAFORMAT_UTF8) && | ||
1709 | (format != EXTRACTOR_METAFORMAT_C_STRING)) | ||
1710 | return 0; | ||
1711 | /* Keywords must be at least 3 characters long | ||
1712 | * If given non-utf8 string it will, most likely, find it to be invalid, | ||
1713 | * and will return the length of its valid part, skipping the keyword. | ||
1714 | * If it does - fix the extractor, not this check! | ||
1715 | */if (u8_strcount ((const uint8_t *) data) <= 2) | ||
1716 | return 0; | ||
1717 | if ((EXTRACTOR_METATYPE_MIMETYPE == type) && | ||
1718 | (NULL != (sep = memchr (data, '/', data_len))) && (sep != data)) | ||
1719 | { | ||
1720 | char *xtra; | ||
1721 | |||
1722 | GNUNET_asprintf (&xtra, "mimetype:%.*s", (int) (sep - data), data); | ||
1723 | if (! find_duplicate (xtra, | ||
1724 | (const char **) uri->data.ksk.keywords, | ||
1725 | uri->data.ksk.keywordCount)) | ||
1726 | { | ||
1727 | insert_non_mandatory_keyword (xtra, | ||
1728 | uri->data.ksk.keywords, | ||
1729 | uri->data.ksk.keywordCount); | ||
1730 | uri->data.ksk.keywordCount++; | ||
1731 | } | ||
1732 | GNUNET_free (xtra); | ||
1733 | } | ||
1734 | |||
1735 | normalized_data = normalize_metadata (format, data, data_len); | ||
1736 | if (! find_duplicate (data, | ||
1737 | (const char **) uri->data.ksk.keywords, | ||
1738 | uri->data.ksk.keywordCount)) | ||
1739 | { | ||
1740 | insert_non_mandatory_keyword (data, | ||
1741 | uri->data.ksk.keywords, | ||
1742 | uri->data.ksk.keywordCount); | ||
1743 | uri->data.ksk.keywordCount++; | ||
1744 | } | ||
1745 | if (NULL != normalized_data) | ||
1746 | { | ||
1747 | if (! find_duplicate (normalized_data, | ||
1748 | (const char **) uri->data.ksk.keywords, | ||
1749 | uri->data.ksk.keywordCount)) | ||
1750 | { | ||
1751 | insert_non_mandatory_keyword (normalized_data, | ||
1752 | uri->data.ksk.keywords, | ||
1753 | uri->data.ksk.keywordCount); | ||
1754 | uri->data.ksk.keywordCount++; | ||
1755 | } | ||
1756 | GNUNET_free (normalized_data); | ||
1757 | } | ||
1758 | return 0; | ||
1759 | } | ||
1760 | |||
1761 | |||
1762 | /** | ||
1763 | * Construct a keyword-URI from meta-data (take all entries | ||
1764 | * in the meta-data and construct one large keyword URI | ||
1765 | * that lists all keywords that can be found in the meta-data). | ||
1766 | * | ||
1767 | * @param md metadata to use | ||
1768 | * @return NULL on error, otherwise a KSK URI | ||
1769 | */ | ||
1770 | struct GNUNET_FS_Uri * | ||
1771 | GNUNET_FS_uri_ksk_create_from_meta_data ( | ||
1772 | const struct GNUNET_FS_MetaData *md) | ||
1773 | { | ||
1774 | struct GNUNET_FS_Uri *ret; | ||
1775 | char *filename; | ||
1776 | char *full_name = NULL; | ||
1777 | char *ss; | ||
1778 | int ent; | ||
1779 | int tok_keywords = 0; | ||
1780 | int paren_keywords = 0; | ||
1781 | |||
1782 | if (NULL == md) | ||
1783 | return NULL; | ||
1784 | ret = GNUNET_new (struct GNUNET_FS_Uri); | ||
1785 | ret->type = GNUNET_FS_URI_KSK; | ||
1786 | ent = GNUNET_FS_meta_data_iterate (md, NULL, NULL); | ||
1787 | if (ent > 0) | ||
1788 | { | ||
1789 | full_name = GNUNET_FS_meta_data_get_first_by_types ( | ||
1790 | md, | ||
1791 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME, | ||
1792 | -1); | ||
1793 | if (NULL != full_name) | ||
1794 | { | ||
1795 | filename = full_name; | ||
1796 | while (NULL != (ss = strstr (filename, DIR_SEPARATOR_STR))) | ||
1797 | filename = ss + 1; | ||
1798 | tok_keywords = get_keywords_from_tokens (filename, NULL, 0); | ||
1799 | paren_keywords = get_keywords_from_parens (filename, NULL, 0); | ||
1800 | } | ||
1801 | /* x3 because there might be a normalized variant of every keyword, | ||
1802 | plus theoretically one more for mime... */ | ||
1803 | ret->data.ksk.keywords = | ||
1804 | GNUNET_new_array ((ent + tok_keywords + paren_keywords) * 3, char *); | ||
1805 | GNUNET_FS_meta_data_iterate (md, &gather_uri_data, ret); | ||
1806 | } | ||
1807 | if (tok_keywords > 0) | ||
1808 | ret->data.ksk.keywordCount += | ||
1809 | get_keywords_from_tokens (filename, | ||
1810 | ret->data.ksk.keywords, | ||
1811 | ret->data.ksk.keywordCount); | ||
1812 | if (paren_keywords > 0) | ||
1813 | ret->data.ksk.keywordCount += | ||
1814 | get_keywords_from_parens (filename, | ||
1815 | ret->data.ksk.keywords, | ||
1816 | ret->data.ksk.keywordCount); | ||
1817 | if (ent > 0) | ||
1818 | GNUNET_free (full_name); | ||
1819 | return ret; | ||
1820 | } | ||
1821 | |||
1822 | |||
1823 | /** | ||
1824 | * In URI-encoding, does the given character | ||
1825 | * need to be encoded using %-encoding? | ||
1826 | */ | ||
1827 | static int | ||
1828 | needs_percent (char c) | ||
1829 | { | ||
1830 | return(! ((isalnum ((unsigned char) c)) || (c == '-') || (c == '_') || | ||
1831 | (c == '.') || (c == '~'))); | ||
1832 | } | ||
1833 | |||
1834 | |||
1835 | /** | ||
1836 | * Convert a KSK URI to a string. | ||
1837 | * | ||
1838 | * @param uri the URI to convert | ||
1839 | * @return NULL on error (i.e. keywordCount == 0) | ||
1840 | */ | ||
1841 | static char * | ||
1842 | uri_ksk_to_string (const struct GNUNET_FS_Uri *uri) | ||
1843 | { | ||
1844 | char **keywords; | ||
1845 | unsigned int keywordCount; | ||
1846 | size_t n; | ||
1847 | char *ret; | ||
1848 | unsigned int i; | ||
1849 | unsigned int j; | ||
1850 | unsigned int wpos; | ||
1851 | size_t slen; | ||
1852 | const char *keyword; | ||
1853 | |||
1854 | if (uri->type != GNUNET_FS_URI_KSK) | ||
1855 | return NULL; | ||
1856 | keywords = uri->data.ksk.keywords; | ||
1857 | keywordCount = uri->data.ksk.keywordCount; | ||
1858 | n = keywordCount + strlen (GNUNET_FS_URI_PREFIX) | ||
1859 | + strlen (GNUNET_FS_URI_KSK_INFIX) + 1; | ||
1860 | for (i = 0; i < keywordCount; i++) | ||
1861 | { | ||
1862 | keyword = keywords[i]; | ||
1863 | slen = strlen (keyword); | ||
1864 | n += slen; | ||
1865 | for (j = 0; j < slen; j++) | ||
1866 | { | ||
1867 | if ((j == 0) && (keyword[j] == ' ')) | ||
1868 | { | ||
1869 | n--; | ||
1870 | continue; /* skip leading space */ | ||
1871 | } | ||
1872 | if (needs_percent (keyword[j])) | ||
1873 | n += 2; /* will use %-encoding */ | ||
1874 | } | ||
1875 | } | ||
1876 | ret = GNUNET_malloc (n); | ||
1877 | strcpy (ret, GNUNET_FS_URI_PREFIX); | ||
1878 | strcat (ret, GNUNET_FS_URI_KSK_INFIX); | ||
1879 | wpos = strlen (ret); | ||
1880 | for (i = 0; i < keywordCount; i++) | ||
1881 | { | ||
1882 | keyword = keywords[i]; | ||
1883 | slen = strlen (keyword); | ||
1884 | for (j = 0; j < slen; j++) | ||
1885 | { | ||
1886 | if ((j == 0) && (keyword[j] == ' ')) | ||
1887 | continue; /* skip leading space */ | ||
1888 | if (needs_percent (keyword[j])) | ||
1889 | { | ||
1890 | sprintf (&ret[wpos], "%%%02X", (unsigned char) keyword[j]); | ||
1891 | wpos += 3; | ||
1892 | } | ||
1893 | else | ||
1894 | { | ||
1895 | ret[wpos++] = keyword[j]; | ||
1896 | } | ||
1897 | } | ||
1898 | if (i != keywordCount - 1) | ||
1899 | ret[wpos++] = '+'; | ||
1900 | } | ||
1901 | return ret; | ||
1902 | } | ||
1903 | |||
1904 | |||
1905 | /** | ||
1906 | * Convert SKS URI to a string. | ||
1907 | * | ||
1908 | * @param uri sks uri to convert | ||
1909 | * @return NULL on error | ||
1910 | */ | ||
1911 | static char * | ||
1912 | uri_sks_to_string (const struct GNUNET_FS_Uri *uri) | ||
1913 | { | ||
1914 | char *ret; | ||
1915 | char buf[1024]; | ||
1916 | |||
1917 | if (GNUNET_FS_URI_SKS != uri->type) | ||
1918 | return NULL; | ||
1919 | ret = | ||
1920 | GNUNET_STRINGS_data_to_string (&uri->data.sks.ns, | ||
1921 | sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey), | ||
1922 | buf, | ||
1923 | sizeof(buf)); | ||
1924 | GNUNET_assert (NULL != ret); | ||
1925 | ret[0] = '\0'; | ||
1926 | GNUNET_asprintf (&ret, | ||
1927 | "%s%s%s/%s", | ||
1928 | GNUNET_FS_URI_PREFIX, | ||
1929 | GNUNET_FS_URI_SKS_INFIX, | ||
1930 | buf, | ||
1931 | uri->data.sks.identifier); | ||
1932 | return ret; | ||
1933 | } | ||
1934 | |||
1935 | |||
1936 | /** | ||
1937 | * Convert a CHK URI to a string. | ||
1938 | * | ||
1939 | * @param uri chk uri to convert | ||
1940 | * @return NULL on error | ||
1941 | */ | ||
1942 | static char * | ||
1943 | uri_chk_to_string (const struct GNUNET_FS_Uri *uri) | ||
1944 | { | ||
1945 | const struct FileIdentifier *fi; | ||
1946 | char *ret; | ||
1947 | struct GNUNET_CRYPTO_HashAsciiEncoded keyhash; | ||
1948 | struct GNUNET_CRYPTO_HashAsciiEncoded queryhash; | ||
1949 | |||
1950 | if (uri->type != GNUNET_FS_URI_CHK) | ||
1951 | return NULL; | ||
1952 | fi = &uri->data.chk; | ||
1953 | GNUNET_CRYPTO_hash_to_enc (&fi->chk.key, &keyhash); | ||
1954 | GNUNET_CRYPTO_hash_to_enc (&fi->chk.query, &queryhash); | ||
1955 | |||
1956 | GNUNET_asprintf (&ret, | ||
1957 | "%s%s%s.%s.%llu", | ||
1958 | GNUNET_FS_URI_PREFIX, | ||
1959 | GNUNET_FS_URI_CHK_INFIX, | ||
1960 | (const char *) &keyhash, | ||
1961 | (const char *) &queryhash, | ||
1962 | (unsigned long long) GNUNET_ntohll (fi->file_length)); | ||
1963 | return ret; | ||
1964 | } | ||
1965 | |||
1966 | |||
1967 | /** | ||
1968 | * Convert a LOC URI to a string. | ||
1969 | * | ||
1970 | * @param uri loc uri to convert | ||
1971 | * @return NULL on error | ||
1972 | */ | ||
1973 | static char * | ||
1974 | uri_loc_to_string (const struct GNUNET_FS_Uri *uri) | ||
1975 | { | ||
1976 | char *ret; | ||
1977 | struct GNUNET_CRYPTO_HashAsciiEncoded keyhash; | ||
1978 | struct GNUNET_CRYPTO_HashAsciiEncoded queryhash; | ||
1979 | char *peer_id; | ||
1980 | char peer_sig[SIGNATURE_ASCII_LENGTH + 1]; | ||
1981 | |||
1982 | GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.key, &keyhash); | ||
1983 | GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.query, &queryhash); | ||
1984 | peer_id = | ||
1985 | GNUNET_CRYPTO_eddsa_public_key_to_string (&uri->data.loc.peer.public_key); | ||
1986 | GNUNET_assert ( | ||
1987 | NULL != | ||
1988 | GNUNET_STRINGS_data_to_string (&uri->data.loc.contentSignature, | ||
1989 | sizeof(struct GNUNET_CRYPTO_EddsaSignature), | ||
1990 | peer_sig, | ||
1991 | sizeof(peer_sig))); | ||
1992 | GNUNET_asprintf (&ret, | ||
1993 | "%s%s%s.%s.%llu.%s.%s.%llu", | ||
1994 | GNUNET_FS_URI_PREFIX, | ||
1995 | GNUNET_FS_URI_LOC_INFIX, | ||
1996 | (const char *) &keyhash, | ||
1997 | (const char *) &queryhash, | ||
1998 | (unsigned long long) GNUNET_ntohll ( | ||
1999 | uri->data.loc.fi.file_length), | ||
2000 | peer_id, | ||
2001 | peer_sig, | ||
2002 | (unsigned long long) | ||
2003 | uri->data.loc.expirationTime.abs_value_us | ||
2004 | / 1000000LL); | ||
2005 | GNUNET_free (peer_id); | ||
2006 | return ret; | ||
2007 | } | ||
2008 | |||
2009 | |||
2010 | /** | ||
2011 | * Convert a URI to a UTF-8 String. | ||
2012 | * | ||
2013 | * @param uri uri to convert to a string | ||
2014 | * @return the UTF-8 string | ||
2015 | */ | ||
2016 | char * | ||
2017 | GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri) | ||
2018 | { | ||
2019 | if (uri == NULL) | ||
2020 | { | ||
2021 | GNUNET_break (0); | ||
2022 | return NULL; | ||
2023 | } | ||
2024 | switch (uri->type) | ||
2025 | { | ||
2026 | case GNUNET_FS_URI_KSK: | ||
2027 | return uri_ksk_to_string (uri); | ||
2028 | |||
2029 | case GNUNET_FS_URI_SKS: | ||
2030 | return uri_sks_to_string (uri); | ||
2031 | |||
2032 | case GNUNET_FS_URI_CHK: | ||
2033 | return uri_chk_to_string (uri); | ||
2034 | |||
2035 | case GNUNET_FS_URI_LOC: | ||
2036 | return uri_loc_to_string (uri); | ||
2037 | |||
2038 | default: | ||
2039 | GNUNET_break (0); | ||
2040 | return NULL; | ||
2041 | } | ||
2042 | } | ||
2043 | |||
2044 | |||
2045 | /* 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 | */ | ||
41 | struct 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 | */ | ||
69 | static int ret; | ||
70 | |||
71 | /** | ||
72 | * Are we running 'verbosely'? | ||
73 | */ | ||
74 | static unsigned int verbose; | ||
75 | |||
76 | /** | ||
77 | * Configuration to use. | ||
78 | */ | ||
79 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
80 | |||
81 | /** | ||
82 | * Name of the configuration file. | ||
83 | */ | ||
84 | static char *cfg_filename; | ||
85 | |||
86 | /** | ||
87 | * Disable extractor option to use for publishing. | ||
88 | */ | ||
89 | static int disable_extractor; | ||
90 | |||
91 | /** | ||
92 | * Disable creation time option to use for publishing. | ||
93 | */ | ||
94 | static int do_disable_creation_time; | ||
95 | |||
96 | /** | ||
97 | * Handle for the main task that does scanning and working. | ||
98 | */ | ||
99 | static struct GNUNET_SCHEDULER_Task *run_task; | ||
100 | |||
101 | /** | ||
102 | * Anonymity level option to use for publishing. | ||
103 | */ | ||
104 | static unsigned int anonymity_level = 1; | ||
105 | |||
106 | /** | ||
107 | * Content priority option to use for publishing. | ||
108 | */ | ||
109 | static unsigned int content_priority = 365; | ||
110 | |||
111 | /** | ||
112 | * Replication level option to use for publishing. | ||
113 | */ | ||
114 | static unsigned int replication_level = 1; | ||
115 | |||
116 | /** | ||
117 | * Top-level directory we monitor to auto-publish. | ||
118 | */ | ||
119 | static const char *dir_name; | ||
120 | |||
121 | /** | ||
122 | * Head of linked list of files still to publish. | ||
123 | */ | ||
124 | static struct WorkItem *work_head; | ||
125 | |||
126 | /** | ||
127 | * Tail of linked list of files still to publish. | ||
128 | */ | ||
129 | static struct WorkItem *work_tail; | ||
130 | |||
131 | /** | ||
132 | * Map from the hash of the filename (!) to a `struct WorkItem` | ||
133 | * that was finished. | ||
134 | */ | ||
135 | static struct GNUNET_CONTAINER_MultiHashMap *work_finished; | ||
136 | |||
137 | /** | ||
138 | * Set to #GNUNET_YES if we are shutting down. | ||
139 | */ | ||
140 | static 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 | */ | ||
147 | static struct GNUNET_TIME_Absolute start_time; | ||
148 | |||
149 | /** | ||
150 | * Pipe used to communicate 'gnunet-publish' completion (SIGCHLD) via signal. | ||
151 | */ | ||
152 | static struct GNUNET_DISK_PipeHandle *sigpipe; | ||
153 | |||
154 | /** | ||
155 | * Handle to the 'gnunet-publish' process that we executed. | ||
156 | */ | ||
157 | static struct GNUNET_OS_Process *publish_proc; | ||
158 | |||
159 | |||
160 | /** | ||
161 | * Compute the name of the state database file we will use. | ||
162 | */ | ||
163 | static char * | ||
164 | get_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 | */ | ||
181 | static void | ||
182 | load_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; | ||
229 | error: | ||
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 | */ | ||
248 | static int | ||
249 | write_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 | */ | ||
274 | static void | ||
275 | save_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 | */ | ||
315 | static void | ||
316 | do_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 | */ | ||
335 | static void | ||
336 | schedule_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 | */ | ||
345 | static void | ||
346 | maint_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 | */ | ||
424 | static void | ||
425 | sighandler_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 | */ | ||
445 | static void | ||
446 | work (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 | */ | ||
514 | static int | ||
515 | determine_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 | */ | ||
561 | static int | ||
562 | add_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 | */ | ||
604 | static void | ||
605 | scan (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 | */ | ||
617 | static void | ||
618 | schedule_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 | */ | ||
650 | static void | ||
651 | run (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 | */ | ||
685 | static int | ||
686 | free_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 | */ | ||
703 | int | ||
704 | main (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 b99933cfa..000000000 --- a/src/fs/gnunet-daemon-fsprofiler.c +++ /dev/null | |||
@@ -1,673 +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 | |||
31 | #include "gnunet_fs_service.h" | ||
32 | #include "gnunet_statistics_service.h" | ||
33 | |||
34 | /** | ||
35 | * We use 'patterns' of the form (x,y,t) to specify desired download/publish | ||
36 | * activities of a peer. They are stored in a DLL. | ||
37 | */ | ||
38 | struct Pattern | ||
39 | { | ||
40 | /** | ||
41 | * Kept in a DLL. | ||
42 | */ | ||
43 | struct Pattern *next; | ||
44 | |||
45 | /** | ||
46 | * Kept in a DLL. | ||
47 | */ | ||
48 | struct Pattern *prev; | ||
49 | |||
50 | /** | ||
51 | * Execution context for the pattern (FS-handle to the operation). | ||
52 | */ | ||
53 | void *ctx; | ||
54 | |||
55 | /** | ||
56 | * Secondary execution context for the pattern (FS-handle to the operation). | ||
57 | */ | ||
58 | void *sctx; | ||
59 | |||
60 | /** | ||
61 | * When did the operation start? | ||
62 | */ | ||
63 | struct GNUNET_TIME_Absolute start_time; | ||
64 | |||
65 | /** | ||
66 | * With how much delay should this operation be started? | ||
67 | */ | ||
68 | struct GNUNET_TIME_Relative delay; | ||
69 | |||
70 | /** | ||
71 | * Task to run the operation. | ||
72 | */ | ||
73 | struct GNUNET_SCHEDULER_Task *task; | ||
74 | |||
75 | /** | ||
76 | * Secondary task to run the operation. | ||
77 | */ | ||
78 | struct GNUNET_SCHEDULER_Task *stask; | ||
79 | |||
80 | /** | ||
81 | * X-value. | ||
82 | */ | ||
83 | unsigned long long x; | ||
84 | |||
85 | /** | ||
86 | * Y-value. | ||
87 | */ | ||
88 | unsigned long long y; | ||
89 | }; | ||
90 | |||
91 | |||
92 | /** | ||
93 | * Return value from 'main'. | ||
94 | */ | ||
95 | static int global_ret; | ||
96 | |||
97 | /** | ||
98 | * Configuration we use. | ||
99 | */ | ||
100 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
101 | |||
102 | /** | ||
103 | * Handle to the statistics service. | ||
104 | */ | ||
105 | static struct GNUNET_STATISTICS_Handle *stats_handle; | ||
106 | |||
107 | /** | ||
108 | * Peer's FS handle. | ||
109 | */ | ||
110 | static struct GNUNET_FS_Handle *fs_handle; | ||
111 | |||
112 | /** | ||
113 | * Unique number for this peer in the testbed. | ||
114 | */ | ||
115 | static unsigned long long my_peerid; | ||
116 | |||
117 | /** | ||
118 | * Desired anonymity level. | ||
119 | */ | ||
120 | static unsigned long long anonymity_level; | ||
121 | |||
122 | /** | ||
123 | * Desired replication level. | ||
124 | */ | ||
125 | static unsigned long long replication_level; | ||
126 | |||
127 | /** | ||
128 | * String describing which publishing operations this peer should | ||
129 | * perform. The format is "(SIZE,SEED,TIME)*", for example: | ||
130 | * "(1,5,0)(7,3,13)" means to publish a file with 1 byte and | ||
131 | * seed/keyword 5 immediately and another file with 7 bytes and | ||
132 | * seed/keyword 3 after 13 ms. | ||
133 | */ | ||
134 | static char *publish_pattern; | ||
135 | |||
136 | /** | ||
137 | * Head of the DLL of publish patterns. | ||
138 | */ | ||
139 | static struct Pattern *publish_head; | ||
140 | |||
141 | /** | ||
142 | * Tail of the DLL of publish patterns. | ||
143 | */ | ||
144 | static struct Pattern *publish_tail; | ||
145 | |||
146 | /** | ||
147 | * String describing which download operations this peer should | ||
148 | * perform. The format is "(KEYWORD,SIZE,DELAY)*"; for example, | ||
149 | * "(1,7,3)(3,8,8)" means to download one file of 7 bytes under | ||
150 | * keyword "1" starting the search after 3 ms; and another one of 8 | ||
151 | * bytes under keyword '3' starting after 8 ms. The file size is | ||
152 | * used to determine which search result(s) should be used or ignored. | ||
153 | */ | ||
154 | static char *download_pattern; | ||
155 | |||
156 | /** | ||
157 | * Head of the DLL of publish patterns. | ||
158 | */ | ||
159 | static struct Pattern *download_head; | ||
160 | |||
161 | /** | ||
162 | * Tail of the DLL of publish patterns. | ||
163 | */ | ||
164 | static struct Pattern *download_tail; | ||
165 | |||
166 | |||
167 | /** | ||
168 | * Parse a pattern string and store the corresponding | ||
169 | * 'struct Pattern' in the given head/tail. | ||
170 | * | ||
171 | * @param head where to store the head | ||
172 | * @param tail where to store the tail | ||
173 | * @param pattern pattern to parse | ||
174 | * @return GNUNET_OK on success | ||
175 | */ | ||
176 | static int | ||
177 | parse_pattern (struct Pattern **head, | ||
178 | struct Pattern **tail, | ||
179 | const char *pattern) | ||
180 | { | ||
181 | struct Pattern *p; | ||
182 | unsigned long long x; | ||
183 | unsigned long long y; | ||
184 | unsigned long long t; | ||
185 | |||
186 | while (3 == sscanf (pattern, | ||
187 | "(%llu,%llu,%llu)", | ||
188 | &x, &y, &t)) | ||
189 | { | ||
190 | p = GNUNET_new (struct Pattern); | ||
191 | p->x = x; | ||
192 | p->y = y; | ||
193 | p->delay.rel_value_us = (uint64_t) t; | ||
194 | GNUNET_CONTAINER_DLL_insert (*head, *tail, p); | ||
195 | pattern = strstr (pattern, ")"); | ||
196 | GNUNET_assert (NULL != pattern); | ||
197 | pattern++; | ||
198 | } | ||
199 | return (0 == strlen (pattern)) ? GNUNET_OK : GNUNET_SYSERR; | ||
200 | } | ||
201 | |||
202 | |||
203 | /** | ||
204 | * Create a KSK URI from a number. | ||
205 | * | ||
206 | * @param kval the number | ||
207 | * @return corresponding KSK URI | ||
208 | */ | ||
209 | static struct GNUNET_FS_Uri * | ||
210 | make_keywords (uint64_t kval) | ||
211 | { | ||
212 | char kw[128]; | ||
213 | |||
214 | GNUNET_snprintf (kw, sizeof(kw), | ||
215 | "%llu", (unsigned long long) kval); | ||
216 | return GNUNET_FS_uri_ksk_create (kw, NULL); | ||
217 | } | ||
218 | |||
219 | |||
220 | /** | ||
221 | * Create a file of the given length with a deterministic amount | ||
222 | * of data to be published under keyword 'kval'. | ||
223 | * | ||
224 | * @param length number of bytes in the file | ||
225 | * @param kval keyword value and seed for the data of the file | ||
226 | * @param ctx context to pass to 'fi' | ||
227 | * @return file information handle for the file | ||
228 | */ | ||
229 | static struct GNUNET_FS_FileInformation * | ||
230 | make_file (uint64_t length, | ||
231 | uint64_t kval, | ||
232 | void *ctx) | ||
233 | { | ||
234 | struct GNUNET_FS_FileInformation *fi; | ||
235 | struct GNUNET_FS_BlockOptions bo; | ||
236 | char *data; | ||
237 | struct GNUNET_FS_Uri *keywords; | ||
238 | unsigned long long i; | ||
239 | uint64_t xor; | ||
240 | |||
241 | data = NULL; /* to make compilers happy */ | ||
242 | if ((0 != length) && | ||
243 | (NULL == (data = GNUNET_malloc_large ((size_t) length)))) | ||
244 | return NULL; | ||
245 | /* initialize data with 'unique' data only depending on 'kval' and 'size', | ||
246 | making sure that blocks do not repeat */ | ||
247 | for (i = 0; i < length; i += 8) | ||
248 | { | ||
249 | xor = length ^ kval ^ (uint64_t) (i / 32 / 1024); | ||
250 | GNUNET_memcpy (&data[i], &xor, GNUNET_MIN (length - i, sizeof(uint64_t))); | ||
251 | } | ||
252 | bo.expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS); | ||
253 | bo.anonymity_level = (uint32_t) anonymity_level; | ||
254 | bo.content_priority = 128; | ||
255 | bo.replication_level = (uint32_t) replication_level; | ||
256 | keywords = make_keywords (kval); | ||
257 | fi = GNUNET_FS_file_information_create_from_data (fs_handle, | ||
258 | ctx, | ||
259 | length, | ||
260 | data, keywords, | ||
261 | NULL, GNUNET_NO, &bo); | ||
262 | GNUNET_FS_uri_destroy (keywords); | ||
263 | return fi; | ||
264 | } | ||
265 | |||
266 | |||
267 | /** | ||
268 | * Task run during shutdown. | ||
269 | * | ||
270 | * @param cls unused | ||
271 | */ | ||
272 | static void | ||
273 | shutdown_task (void *cls) | ||
274 | { | ||
275 | struct Pattern *p; | ||
276 | |||
277 | while (NULL != (p = publish_head)) | ||
278 | { | ||
279 | if (NULL != p->task) | ||
280 | GNUNET_SCHEDULER_cancel (p->task); | ||
281 | if (NULL != p->ctx) | ||
282 | GNUNET_FS_publish_stop (p->ctx); | ||
283 | GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p); | ||
284 | GNUNET_free (p); | ||
285 | } | ||
286 | while (NULL != (p = download_head)) | ||
287 | { | ||
288 | if (NULL != p->task) | ||
289 | GNUNET_SCHEDULER_cancel (p->task); | ||
290 | if (NULL != p->stask) | ||
291 | GNUNET_SCHEDULER_cancel (p->stask); | ||
292 | if (NULL != p->ctx) | ||
293 | GNUNET_FS_download_stop (p->ctx, GNUNET_YES); | ||
294 | if (NULL != p->sctx) | ||
295 | GNUNET_FS_search_stop (p->sctx); | ||
296 | GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p); | ||
297 | GNUNET_free (p); | ||
298 | } | ||
299 | if (NULL != fs_handle) | ||
300 | { | ||
301 | GNUNET_FS_stop (fs_handle); | ||
302 | fs_handle = NULL; | ||
303 | } | ||
304 | if (NULL != stats_handle) | ||
305 | { | ||
306 | GNUNET_STATISTICS_destroy (stats_handle, GNUNET_YES); | ||
307 | stats_handle = NULL; | ||
308 | } | ||
309 | } | ||
310 | |||
311 | |||
312 | /** | ||
313 | * Task run when a publish operation should be stopped. | ||
314 | * | ||
315 | * @param cls the 'struct Pattern' of the publish operation to stop | ||
316 | */ | ||
317 | static void | ||
318 | publish_stop_task (void *cls) | ||
319 | { | ||
320 | struct Pattern *p = cls; | ||
321 | |||
322 | p->task = NULL; | ||
323 | GNUNET_FS_publish_stop (p->ctx); | ||
324 | } | ||
325 | |||
326 | |||
327 | /** | ||
328 | * Task run when a download operation should be stopped. | ||
329 | * | ||
330 | * @param cls the 'struct Pattern' of the download operation to stop | ||
331 | */ | ||
332 | static void | ||
333 | download_stop_task (void *cls) | ||
334 | { | ||
335 | struct Pattern *p = cls; | ||
336 | |||
337 | p->task = NULL; | ||
338 | GNUNET_FS_download_stop (p->ctx, GNUNET_YES); | ||
339 | } | ||
340 | |||
341 | |||
342 | /** | ||
343 | * Task run when a download operation should be stopped. | ||
344 | * | ||
345 | * @param cls the 'struct Pattern' of the download operation to stop | ||
346 | */ | ||
347 | static void | ||
348 | search_stop_task (void *cls) | ||
349 | { | ||
350 | struct Pattern *p = cls; | ||
351 | |||
352 | p->stask = NULL; | ||
353 | GNUNET_FS_search_stop (p->sctx); | ||
354 | } | ||
355 | |||
356 | |||
357 | /** | ||
358 | * Notification of FS to a client about the progress of an | ||
359 | * operation. Callbacks of this type will be used for uploads, | ||
360 | * downloads and searches. Some of the arguments depend a bit | ||
361 | * in their meaning on the context in which the callback is used. | ||
362 | * | ||
363 | * @param cls closure | ||
364 | * @param info details about the event, specifying the event type | ||
365 | * and various bits about the event | ||
366 | * @return client-context (for the next progress call | ||
367 | * for this operation; should be set to NULL for | ||
368 | * SUSPEND and STOPPED events). The value returned | ||
369 | * will be passed to future callbacks in the respective | ||
370 | * field in the GNUNET_FS_ProgressInfo struct. | ||
371 | */ | ||
372 | static void * | ||
373 | progress_cb (void *cls, | ||
374 | const struct GNUNET_FS_ProgressInfo *info) | ||
375 | { | ||
376 | struct Pattern *p; | ||
377 | const struct GNUNET_FS_Uri *uri; | ||
378 | |||
379 | switch (info->status) | ||
380 | { | ||
381 | case GNUNET_FS_STATUS_PUBLISH_START: | ||
382 | case GNUNET_FS_STATUS_PUBLISH_PROGRESS: | ||
383 | p = info->value.publish.cctx; | ||
384 | return p; | ||
385 | |||
386 | case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY: | ||
387 | p = info->value.publish.cctx; | ||
388 | return p; | ||
389 | |||
390 | case GNUNET_FS_STATUS_PUBLISH_ERROR: | ||
391 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
392 | "Publishing failed\n"); | ||
393 | GNUNET_STATISTICS_update (stats_handle, | ||
394 | "# failed publish operations", 1, GNUNET_NO); | ||
395 | p = info->value.publish.cctx; | ||
396 | p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p); | ||
397 | return p; | ||
398 | |||
399 | case GNUNET_FS_STATUS_PUBLISH_COMPLETED: | ||
400 | p = info->value.publish.cctx; | ||
401 | GNUNET_STATISTICS_update (stats_handle, | ||
402 | "# publishing time (ms)", | ||
403 | (long long) GNUNET_TIME_absolute_get_duration ( | ||
404 | p->start_time).rel_value_us / 1000LL, | ||
405 | GNUNET_NO); | ||
406 | p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p); | ||
407 | return p; | ||
408 | |||
409 | case GNUNET_FS_STATUS_PUBLISH_STOPPED: | ||
410 | p = info->value.publish.cctx; | ||
411 | p->ctx = NULL; | ||
412 | GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p); | ||
413 | GNUNET_free (p); | ||
414 | return NULL; | ||
415 | |||
416 | case GNUNET_FS_STATUS_DOWNLOAD_START: | ||
417 | case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: | ||
418 | case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: | ||
419 | case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: | ||
420 | p = info->value.download.cctx; | ||
421 | return p; | ||
422 | |||
423 | case GNUNET_FS_STATUS_DOWNLOAD_ERROR: | ||
424 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
425 | "Download failed\n"); | ||
426 | GNUNET_STATISTICS_update (stats_handle, | ||
427 | "# failed downloads", 1, GNUNET_NO); | ||
428 | p = info->value.download.cctx; | ||
429 | p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p); | ||
430 | return p; | ||
431 | |||
432 | case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: | ||
433 | p = info->value.download.cctx; | ||
434 | GNUNET_STATISTICS_update (stats_handle, | ||
435 | "# download time (ms)", | ||
436 | (long long) GNUNET_TIME_absolute_get_duration ( | ||
437 | p->start_time).rel_value_us / 1000LL, | ||
438 | GNUNET_NO); | ||
439 | p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p); | ||
440 | return p; | ||
441 | |||
442 | case GNUNET_FS_STATUS_DOWNLOAD_STOPPED: | ||
443 | p = info->value.download.cctx; | ||
444 | p->ctx = NULL; | ||
445 | if (NULL == p->sctx) | ||
446 | { | ||
447 | GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p); | ||
448 | GNUNET_free (p); | ||
449 | } | ||
450 | return NULL; | ||
451 | |||
452 | case GNUNET_FS_STATUS_SEARCH_START: | ||
453 | case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE: | ||
454 | p = info->value.search.cctx; | ||
455 | return p; | ||
456 | |||
457 | case GNUNET_FS_STATUS_SEARCH_RESULT: | ||
458 | p = info->value.search.cctx; | ||
459 | uri = info->value.search.specifics.result.uri; | ||
460 | if (GNUNET_YES != GNUNET_FS_uri_test_chk (uri)) | ||
461 | return NULL; /* not what we want */ | ||
462 | if (p->y != GNUNET_FS_uri_chk_get_file_size (uri)) | ||
463 | return NULL; /* not what we want */ | ||
464 | GNUNET_STATISTICS_update (stats_handle, | ||
465 | "# search time (ms)", | ||
466 | (long long) GNUNET_TIME_absolute_get_duration ( | ||
467 | p->start_time).rel_value_us / 1000LL, | ||
468 | GNUNET_NO); | ||
469 | p->start_time = GNUNET_TIME_absolute_get (); | ||
470 | p->ctx = GNUNET_FS_download_start (fs_handle, uri, | ||
471 | NULL, NULL, NULL, | ||
472 | 0, GNUNET_FS_uri_chk_get_file_size (uri), | ||
473 | anonymity_level, | ||
474 | GNUNET_FS_DOWNLOAD_NO_TEMPORARIES, | ||
475 | p, | ||
476 | NULL); | ||
477 | p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p); | ||
478 | return NULL; | ||
479 | |||
480 | case GNUNET_FS_STATUS_SEARCH_UPDATE: | ||
481 | case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED: | ||
482 | return NULL; /* don't care */ | ||
483 | |||
484 | case GNUNET_FS_STATUS_SEARCH_ERROR: | ||
485 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
486 | "Search failed\n"); | ||
487 | GNUNET_STATISTICS_update (stats_handle, | ||
488 | "# failed searches", 1, GNUNET_NO); | ||
489 | p = info->value.search.cctx; | ||
490 | p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p); | ||
491 | return p; | ||
492 | |||
493 | case GNUNET_FS_STATUS_SEARCH_STOPPED: | ||
494 | p = info->value.search.cctx; | ||
495 | p->sctx = NULL; | ||
496 | if (NULL == p->ctx) | ||
497 | { | ||
498 | GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p); | ||
499 | GNUNET_free (p); | ||
500 | } | ||
501 | return NULL; | ||
502 | |||
503 | default: | ||
504 | /* unexpected event during profiling */ | ||
505 | GNUNET_break (0); | ||
506 | return NULL; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | |||
511 | /** | ||
512 | * Start publish operation. | ||
513 | * | ||
514 | * @param cls the 'struct Pattern' specifying the operation to perform | ||
515 | */ | ||
516 | static void | ||
517 | start_publish (void *cls) | ||
518 | { | ||
519 | struct Pattern *p = cls; | ||
520 | struct GNUNET_FS_FileInformation *fi; | ||
521 | |||
522 | p->task = NULL; | ||
523 | fi = make_file (p->x, p->y, p); | ||
524 | p->start_time = GNUNET_TIME_absolute_get (); | ||
525 | p->ctx = GNUNET_FS_publish_start (fs_handle, | ||
526 | fi, | ||
527 | NULL, NULL, NULL, | ||
528 | GNUNET_FS_PUBLISH_OPTION_NONE); | ||
529 | } | ||
530 | |||
531 | |||
532 | /** | ||
533 | * Start download operation. | ||
534 | * | ||
535 | * @param cls the 'struct Pattern' specifying the operation to perform | ||
536 | */ | ||
537 | static void | ||
538 | start_download (void *cls) | ||
539 | { | ||
540 | struct Pattern *p = cls; | ||
541 | struct GNUNET_FS_Uri *keywords; | ||
542 | |||
543 | p->task = NULL; | ||
544 | keywords = make_keywords (p->x); | ||
545 | p->start_time = GNUNET_TIME_absolute_get (); | ||
546 | p->sctx = GNUNET_FS_search_start (fs_handle, keywords, | ||
547 | anonymity_level, | ||
548 | GNUNET_FS_SEARCH_OPTION_NONE, | ||
549 | p); | ||
550 | } | ||
551 | |||
552 | |||
553 | /** | ||
554 | * @brief Main function that will be run by the scheduler. | ||
555 | * | ||
556 | * @param cls closure | ||
557 | * @param args remaining command-line arguments | ||
558 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
559 | * @param cfg_ configuration | ||
560 | */ | ||
561 | static void | ||
562 | run (void *cls, char *const *args GNUNET_UNUSED, | ||
563 | const char *cfgfile GNUNET_UNUSED, | ||
564 | const struct GNUNET_CONFIGURATION_Handle *cfg_) | ||
565 | { | ||
566 | char myoptname[128]; | ||
567 | struct Pattern *p; | ||
568 | |||
569 | cfg = cfg_; | ||
570 | /* Scheduled the task to clean up when shutdown is called */ | ||
571 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
572 | NULL); | ||
573 | |||
574 | if (GNUNET_OK != | ||
575 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
576 | "TESTBED", "PEERID", | ||
577 | &my_peerid)) | ||
578 | { | ||
579 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
580 | "TESTBED", "PEERID"); | ||
581 | global_ret = GNUNET_SYSERR; | ||
582 | GNUNET_SCHEDULER_shutdown (); | ||
583 | return; | ||
584 | } | ||
585 | if (GNUNET_OK != | ||
586 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
587 | "FSPROFILER", "ANONYMITY_LEVEL", | ||
588 | &anonymity_level)) | ||
589 | anonymity_level = 1; | ||
590 | if (GNUNET_OK != | ||
591 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
592 | "FSPROFILER", "REPLICATION_LEVEL", | ||
593 | &replication_level)) | ||
594 | replication_level = 1; | ||
595 | GNUNET_snprintf (myoptname, sizeof(myoptname), | ||
596 | "DOWNLOAD-PATTERN-%llu", my_peerid); | ||
597 | if (GNUNET_OK != | ||
598 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
599 | "FSPROFILER", myoptname, | ||
600 | &download_pattern)) | ||
601 | download_pattern = GNUNET_strdup (""); | ||
602 | GNUNET_snprintf (myoptname, sizeof(myoptname), | ||
603 | "PUBLISH-PATTERN-%llu", my_peerid); | ||
604 | if (GNUNET_OK != | ||
605 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
606 | "FSPROFILER", myoptname, | ||
607 | &publish_pattern)) | ||
608 | publish_pattern = GNUNET_strdup (""); | ||
609 | if ((GNUNET_OK != | ||
610 | parse_pattern (&download_head, | ||
611 | &download_tail, | ||
612 | download_pattern)) || | ||
613 | (GNUNET_OK != | ||
614 | parse_pattern (&publish_head, | ||
615 | &publish_tail, | ||
616 | publish_pattern))) | ||
617 | { | ||
618 | GNUNET_SCHEDULER_shutdown (); | ||
619 | return; | ||
620 | } | ||
621 | |||
622 | stats_handle = GNUNET_STATISTICS_create ("fsprofiler", cfg); | ||
623 | fs_handle = | ||
624 | GNUNET_FS_start (cfg, | ||
625 | "fsprofiler", | ||
626 | &progress_cb, NULL, | ||
627 | GNUNET_FS_FLAGS_NONE, | ||
628 | GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 1, | ||
629 | GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 1, | ||
630 | GNUNET_FS_OPTIONS_END); | ||
631 | if (NULL == fs_handle) | ||
632 | { | ||
633 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
634 | "Could not acquire FS handle. Exiting.\n"); | ||
635 | global_ret = GNUNET_SYSERR; | ||
636 | GNUNET_SCHEDULER_shutdown (); | ||
637 | return; | ||
638 | } | ||
639 | for (p = publish_head; NULL != p; p = p->next) | ||
640 | p->task = GNUNET_SCHEDULER_add_delayed (p->delay, | ||
641 | &start_publish, p); | ||
642 | for (p = download_head; NULL != p; p = p->next) | ||
643 | p->task = GNUNET_SCHEDULER_add_delayed (p->delay, | ||
644 | &start_download, p); | ||
645 | } | ||
646 | |||
647 | |||
648 | /** | ||
649 | * Program that performs various "random" FS activities. | ||
650 | * | ||
651 | * @param argc number of arguments from the command line | ||
652 | * @param argv command line arguments | ||
653 | * @return 0 ok, 1 on error | ||
654 | */ | ||
655 | int | ||
656 | main (int argc, char *const *argv) | ||
657 | { | ||
658 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
659 | GNUNET_GETOPT_OPTION_END | ||
660 | }; | ||
661 | |||
662 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
663 | return 2; | ||
664 | return (GNUNET_OK == | ||
665 | GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-fsprofiler", | ||
666 | gettext_noop | ||
667 | ( | ||
668 | "Daemon to use file-sharing to measure its performance."), | ||
669 | options, &run, NULL)) ? global_ret : 1; | ||
670 | } | ||
671 | |||
672 | |||
673 | /* 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 ab9f2905a..000000000 --- a/src/fs/gnunet-directory.c +++ /dev/null | |||
@@ -1,212 +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 | |||
27 | #include "gnunet_fs_service.h" | ||
28 | |||
29 | static int ret; | ||
30 | |||
31 | /** | ||
32 | * Print a meta data entry. | ||
33 | * | ||
34 | * @param cls closure (unused) | ||
35 | * @param plugin_name name of the plugin that generated the meta data | ||
36 | * @param type type of the keyword | ||
37 | * @param format format of data | ||
38 | * @param data_mime_type mime type of data | ||
39 | * @param data value of the meta data | ||
40 | * @param data_size number of bytes in @a data | ||
41 | * @return always 0 (to continue iterating) | ||
42 | */ | ||
43 | static int | ||
44 | item_printer (void *cls, | ||
45 | const char *plugin_name, | ||
46 | enum EXTRACTOR_MetaType type, | ||
47 | enum EXTRACTOR_MetaFormat format, | ||
48 | const char *data_mime_type, | ||
49 | const char *data, | ||
50 | size_t data_size) | ||
51 | { | ||
52 | if (type == EXTRACTOR_METATYPE_GNUNET_FULL_DATA) | ||
53 | { | ||
54 | printf (_ ("\t<original file embedded in %u bytes of meta data>\n"), | ||
55 | (unsigned int) data_size); | ||
56 | return 0; | ||
57 | } | ||
58 | if ((format != EXTRACTOR_METAFORMAT_UTF8) && | ||
59 | (format != EXTRACTOR_METAFORMAT_C_STRING)) | ||
60 | return 0; | ||
61 | if (type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME) | ||
62 | return 0; | ||
63 | #if HAVE_LIBEXTRACTOR | ||
64 | printf ("\t%20s: %s\n", | ||
65 | dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN, | ||
66 | EXTRACTOR_metatype_to_string (type)), | ||
67 | data); | ||
68 | #else | ||
69 | printf ("\t%20d: %s\n", type, data); | ||
70 | #endif | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Print an entry in a directory. | ||
77 | * | ||
78 | * @param cls closure (not used) | ||
79 | * @param filename name of the file in the directory | ||
80 | * @param uri URI of the file | ||
81 | * @param meta metadata for the file; metadata for | ||
82 | * the directory if everything else is NULL/zero | ||
83 | * @param length length of the available data for the file | ||
84 | * (of type size_t since data must certainly fit | ||
85 | * into memory; if files are larger than size_t | ||
86 | * permits, then they will certainly not be | ||
87 | * embedded with the directory itself). | ||
88 | * @param data data available for the file (length bytes) | ||
89 | */ | ||
90 | static void | ||
91 | print_entry (void *cls, | ||
92 | const char *filename, | ||
93 | const struct GNUNET_FS_Uri *uri, | ||
94 | const struct GNUNET_FS_MetaData *meta, | ||
95 | size_t length, | ||
96 | const void *data) | ||
97 | { | ||
98 | char *string; | ||
99 | char *name; | ||
100 | |||
101 | name = GNUNET_FS_meta_data_get_by_type ( | ||
102 | meta, | ||
103 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); | ||
104 | if (uri == NULL) | ||
105 | { | ||
106 | printf (_ ("Directory `%s' meta data:\n"), name ? name : ""); | ||
107 | GNUNET_FS_meta_data_iterate (meta, &item_printer, NULL); | ||
108 | printf ("\n"); | ||
109 | printf (_ ("Directory `%s' contents:\n"), name ? name : ""); | ||
110 | GNUNET_free (name); | ||
111 | return; | ||
112 | } | ||
113 | string = GNUNET_FS_uri_to_string (uri); | ||
114 | printf ("%s (%s):\n", name ? name : "", string); | ||
115 | GNUNET_free (string); | ||
116 | GNUNET_FS_meta_data_iterate (meta, &item_printer, NULL); | ||
117 | printf ("\n"); | ||
118 | GNUNET_free (name); | ||
119 | } | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Main function that will be run by the scheduler. | ||
124 | * | ||
125 | * @param cls closure | ||
126 | * @param args remaining command-line arguments | ||
127 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
128 | * @param cfg configuration | ||
129 | */ | ||
130 | static void | ||
131 | run (void *cls, | ||
132 | char *const *args, | ||
133 | const char *cfgfile, | ||
134 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
135 | { | ||
136 | struct GNUNET_DISK_MapHandle *map; | ||
137 | struct GNUNET_DISK_FileHandle *h; | ||
138 | void *data; | ||
139 | size_t len; | ||
140 | uint64_t size; | ||
141 | const char *filename; | ||
142 | int i; | ||
143 | |||
144 | if (NULL == args[0]) | ||
145 | { | ||
146 | fprintf (stderr, "%s", _ ("You must specify a filename to inspect.\n")); | ||
147 | ret = 1; | ||
148 | return; | ||
149 | } | ||
150 | i = 0; | ||
151 | while (NULL != (filename = args[i++])) | ||
152 | { | ||
153 | if ((GNUNET_OK != | ||
154 | GNUNET_DISK_file_size (filename, &size, GNUNET_YES, GNUNET_YES)) || | ||
155 | (NULL == (h = GNUNET_DISK_file_open (filename, | ||
156 | GNUNET_DISK_OPEN_READ, | ||
157 | GNUNET_DISK_PERM_NONE)))) | ||
158 | { | ||
159 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
160 | _ ("Failed to read directory `%s'\n"), | ||
161 | filename); | ||
162 | ret = 1; | ||
163 | continue; | ||
164 | } | ||
165 | len = (size_t) size; | ||
166 | data = GNUNET_DISK_file_map (h, &map, GNUNET_DISK_MAP_TYPE_READ, len); | ||
167 | GNUNET_assert (NULL != data); | ||
168 | if (GNUNET_OK != | ||
169 | GNUNET_FS_directory_list_contents (len, data, 0, &print_entry, NULL)) | ||
170 | fprintf (stdout, _ ("`%s' is not a GNUnet directory\n"), filename); | ||
171 | else | ||
172 | printf ("\n"); | ||
173 | GNUNET_DISK_file_unmap (map); | ||
174 | GNUNET_DISK_file_close (h); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * The main function to inspect GNUnet directories. | ||
181 | * | ||
182 | * @param argc number of arguments from the command line | ||
183 | * @param argv command line arguments | ||
184 | * @return 0 ok, 1 on error | ||
185 | */ | ||
186 | int | ||
187 | main (int argc, char *const *argv) | ||
188 | { | ||
189 | static struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
190 | GNUNET_GETOPT_OPTION_END | ||
191 | }; | ||
192 | |||
193 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
194 | return 2; | ||
195 | |||
196 | ret = (GNUNET_OK == | ||
197 | GNUNET_PROGRAM_run (argc, | ||
198 | argv, | ||
199 | "gnunet-directory [OPTIONS] FILENAME", | ||
200 | gettext_noop ( | ||
201 | "Display contents of a GNUnet directory"), | ||
202 | options, | ||
203 | &run, | ||
204 | NULL)) | ||
205 | ? ret | ||
206 | : 1; | ||
207 | GNUNET_free_nz ((void *) argv); | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | |||
212 | /* end of gnunet-directory.c */ | ||
diff --git a/src/fs/gnunet-download.c b/src/fs/gnunet-download.c deleted file mode 100644 index 4694077e9..000000000 --- a/src/fs/gnunet-download.c +++ /dev/null | |||
@@ -1,385 +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 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | |||
32 | static int ret; | ||
33 | |||
34 | static unsigned int verbose; | ||
35 | |||
36 | static int delete_incomplete; | ||
37 | |||
38 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
39 | |||
40 | static struct GNUNET_FS_Handle *ctx; | ||
41 | |||
42 | static struct GNUNET_FS_DownloadContext *dc; | ||
43 | |||
44 | static unsigned int anonymity = 1; | ||
45 | |||
46 | static unsigned int parallelism = 16; | ||
47 | |||
48 | static unsigned int request_parallelism = 4092; | ||
49 | |||
50 | static int do_recursive; | ||
51 | |||
52 | static char *filename; | ||
53 | |||
54 | static int local_only; | ||
55 | |||
56 | |||
57 | static void | ||
58 | cleanup_task (void *cls) | ||
59 | { | ||
60 | GNUNET_FS_stop (ctx); | ||
61 | ctx = NULL; | ||
62 | } | ||
63 | |||
64 | |||
65 | static void | ||
66 | shutdown_task (void *cls) | ||
67 | { | ||
68 | if (NULL != dc) | ||
69 | { | ||
70 | GNUNET_FS_download_stop (dc, delete_incomplete); | ||
71 | dc = NULL; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | |||
76 | /** | ||
77 | * Display progress bar (if tty). | ||
78 | * | ||
79 | * @param x current position in the download | ||
80 | * @param n total size of the download | ||
81 | * @param w desired number of steps in the progress bar | ||
82 | */ | ||
83 | static void | ||
84 | display_bar (unsigned long long x, unsigned long long n, unsigned int w) | ||
85 | { | ||
86 | char buf[w + 20]; | ||
87 | unsigned int p; | ||
88 | unsigned int endeq; | ||
89 | float ratio_complete; | ||
90 | |||
91 | if (0 == isatty (1)) | ||
92 | return; | ||
93 | ratio_complete = x / (float) n; | ||
94 | endeq = ratio_complete * w; | ||
95 | GNUNET_snprintf (buf, sizeof(buf), "%3d%% [", (int) (ratio_complete * 100)); | ||
96 | for (p = 0; p < endeq; p++) | ||
97 | strcat (buf, "="); | ||
98 | for (p = endeq; p < w; p++) | ||
99 | strcat (buf, " "); | ||
100 | strcat (buf, "]\r"); | ||
101 | printf ("%s", buf); | ||
102 | fflush (stdout); | ||
103 | } | ||
104 | |||
105 | |||
106 | /** | ||
107 | * Called by FS client to give information about the progress of an | ||
108 | * operation. | ||
109 | * | ||
110 | * @param cls closure | ||
111 | * @param info details about the event, specifying the event type | ||
112 | * and various bits about the event | ||
113 | * @return client-context (for the next progress call | ||
114 | * for this operation; should be set to NULL for | ||
115 | * SUSPEND and STOPPED events). The value returned | ||
116 | * will be passed to future callbacks in the respective | ||
117 | * field in the `struct GNUNET_FS_ProgressInfo` | ||
118 | */ | ||
119 | static void * | ||
120 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) | ||
121 | { | ||
122 | char *s; | ||
123 | const char *s2; | ||
124 | char *t; | ||
125 | |||
126 | switch (info->status) | ||
127 | { | ||
128 | case GNUNET_FS_STATUS_DOWNLOAD_START: | ||
129 | if (verbose > 1) | ||
130 | fprintf (stderr, | ||
131 | _ ("Starting download `%s'.\n"), | ||
132 | info->value.download.filename); | ||
133 | break; | ||
134 | |||
135 | case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: | ||
136 | if (verbose) | ||
137 | { | ||
138 | s = GNUNET_strdup ( | ||
139 | GNUNET_STRINGS_relative_time_to_string (info->value.download.eta, | ||
140 | GNUNET_YES)); | ||
141 | if (info->value.download.specifics.progress.block_download_duration | ||
142 | .rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) | ||
143 | s2 = _ ("<unknown time>"); | ||
144 | else | ||
145 | s2 = GNUNET_STRINGS_relative_time_to_string (info->value.download | ||
146 | .specifics.progress | ||
147 | .block_download_duration, | ||
148 | GNUNET_YES); | ||
149 | t = GNUNET_STRINGS_byte_size_fancy ( | ||
150 | info->value.download.completed * 1000LL | ||
151 | / (info->value.download.duration.rel_value_us + 1)); | ||
152 | fprintf ( | ||
153 | stdout, | ||
154 | _ ( | ||
155 | "Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"), | ||
156 | info->value.download.filename, | ||
157 | (unsigned long long) info->value.download.completed, | ||
158 | (unsigned long long) info->value.download.size, | ||
159 | s, | ||
160 | t, | ||
161 | s2); | ||
162 | GNUNET_free (s); | ||
163 | GNUNET_free (t); | ||
164 | } | ||
165 | else | ||
166 | { | ||
167 | display_bar (info->value.download.completed, | ||
168 | info->value.download.size, | ||
169 | 60); | ||
170 | } | ||
171 | break; | ||
172 | |||
173 | case GNUNET_FS_STATUS_DOWNLOAD_ERROR: | ||
174 | if (0 != isatty (1)) | ||
175 | fprintf (stdout, "\n"); | ||
176 | fprintf (stderr, | ||
177 | _ ("Error downloading: %s.\n"), | ||
178 | info->value.download.specifics.error.message); | ||
179 | GNUNET_SCHEDULER_shutdown (); | ||
180 | break; | ||
181 | |||
182 | case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: | ||
183 | s = GNUNET_STRINGS_byte_size_fancy ( | ||
184 | info->value.download.completed * 1000 | ||
185 | / (info->value.download.duration.rel_value_us + 1)); | ||
186 | if (0 != isatty (1)) | ||
187 | fprintf (stdout, "\n"); | ||
188 | fprintf (stdout, | ||
189 | _ ("Downloading `%s' done (%s/s).\n"), | ||
190 | info->value.download.filename, | ||
191 | s); | ||
192 | GNUNET_free (s); | ||
193 | if (info->value.download.dc == dc) | ||
194 | GNUNET_SCHEDULER_shutdown (); | ||
195 | break; | ||
196 | |||
197 | case GNUNET_FS_STATUS_DOWNLOAD_STOPPED: | ||
198 | if (info->value.download.dc == dc) | ||
199 | GNUNET_SCHEDULER_add_now (&cleanup_task, NULL); | ||
200 | break; | ||
201 | |||
202 | case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: | ||
203 | case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: | ||
204 | break; | ||
205 | |||
206 | default: | ||
207 | fprintf (stderr, _ ("Unexpected status: %d\n"), info->status); | ||
208 | break; | ||
209 | } | ||
210 | return NULL; | ||
211 | } | ||
212 | |||
213 | |||
214 | /** | ||
215 | * Main function that will be run by the scheduler. | ||
216 | * | ||
217 | * @param cls closure | ||
218 | * @param args remaining command-line arguments | ||
219 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
220 | * @param c configuration | ||
221 | */ | ||
222 | static void | ||
223 | run (void *cls, | ||
224 | char *const *args, | ||
225 | const char *cfgfile, | ||
226 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
227 | { | ||
228 | struct GNUNET_FS_Uri *uri; | ||
229 | char *emsg; | ||
230 | enum GNUNET_FS_DownloadOptions options; | ||
231 | |||
232 | if (NULL == args[0]) | ||
233 | { | ||
234 | fprintf (stderr, "%s", _ ("You need to specify a URI argument.\n")); | ||
235 | return; | ||
236 | } | ||
237 | uri = GNUNET_FS_uri_parse (args[0], &emsg); | ||
238 | if (NULL == uri) | ||
239 | { | ||
240 | fprintf (stderr, _ ("Failed to parse URI: %s\n"), emsg); | ||
241 | GNUNET_free (emsg); | ||
242 | ret = 1; | ||
243 | return; | ||
244 | } | ||
245 | if ((! GNUNET_FS_uri_test_chk (uri)) && (! GNUNET_FS_uri_test_loc (uri))) | ||
246 | { | ||
247 | fprintf (stderr, "%s", _ ("Only CHK or LOC URIs supported.\n")); | ||
248 | ret = 1; | ||
249 | GNUNET_FS_uri_destroy (uri); | ||
250 | return; | ||
251 | } | ||
252 | if (NULL == filename) | ||
253 | { | ||
254 | fprintf (stderr, "%s", _ ("Target filename must be specified.\n")); | ||
255 | ret = 1; | ||
256 | GNUNET_FS_uri_destroy (uri); | ||
257 | return; | ||
258 | } | ||
259 | cfg = c; | ||
260 | ctx = GNUNET_FS_start (cfg, | ||
261 | "gnunet-download", | ||
262 | &progress_cb, | ||
263 | NULL, | ||
264 | GNUNET_FS_FLAGS_NONE, | ||
265 | GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, | ||
266 | parallelism, | ||
267 | GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, | ||
268 | request_parallelism, | ||
269 | GNUNET_FS_OPTIONS_END); | ||
270 | if (NULL == ctx) | ||
271 | { | ||
272 | fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS"); | ||
273 | GNUNET_FS_uri_destroy (uri); | ||
274 | ret = 1; | ||
275 | return; | ||
276 | } | ||
277 | options = GNUNET_FS_DOWNLOAD_OPTION_NONE; | ||
278 | if (do_recursive) | ||
279 | options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE; | ||
280 | if (local_only) | ||
281 | options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY; | ||
282 | dc = GNUNET_FS_download_start (ctx, | ||
283 | uri, | ||
284 | NULL, | ||
285 | filename, | ||
286 | NULL, | ||
287 | 0, | ||
288 | GNUNET_FS_uri_chk_get_file_size (uri), | ||
289 | anonymity, | ||
290 | options, | ||
291 | NULL, | ||
292 | NULL); | ||
293 | GNUNET_FS_uri_destroy (uri); | ||
294 | if (dc == NULL) | ||
295 | { | ||
296 | GNUNET_FS_stop (ctx); | ||
297 | ctx = NULL; | ||
298 | return; | ||
299 | } | ||
300 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
301 | } | ||
302 | |||
303 | |||
304 | /** | ||
305 | * The main function to download GNUnet. | ||
306 | * | ||
307 | * @param argc number of arguments from the command line | ||
308 | * @param argv command line arguments | ||
309 | * @return 0 ok, 1 on error | ||
310 | */ | ||
311 | int | ||
312 | main (int argc, char *const *argv) | ||
313 | { | ||
314 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
315 | { GNUNET_GETOPT_option_uint ('a', | ||
316 | "anonymity", | ||
317 | "LEVEL", | ||
318 | gettext_noop ( | ||
319 | "set the desired LEVEL of receiver-anonymity"), | ||
320 | &anonymity), | ||
321 | |||
322 | GNUNET_GETOPT_option_flag ( | ||
323 | 'D', | ||
324 | "delete-incomplete", | ||
325 | gettext_noop ("delete incomplete downloads (when aborted with CTRL-C)"), | ||
326 | &delete_incomplete), | ||
327 | |||
328 | GNUNET_GETOPT_option_flag ( | ||
329 | 'n', | ||
330 | "no-network", | ||
331 | gettext_noop ("only search the local peer (no P2P network search)"), | ||
332 | &local_only), | ||
333 | GNUNET_GETOPT_option_string ('o', | ||
334 | "output", | ||
335 | "FILENAME", | ||
336 | gettext_noop ("write the file to FILENAME"), | ||
337 | &filename), | ||
338 | GNUNET_GETOPT_option_uint ( | ||
339 | 'p', | ||
340 | "parallelism", | ||
341 | "DOWNLOADS", | ||
342 | gettext_noop ( | ||
343 | "set the maximum number of parallel downloads that is allowed"), | ||
344 | ¶llelism), | ||
345 | GNUNET_GETOPT_option_uint ( | ||
346 | 'r', | ||
347 | "request-parallelism", | ||
348 | "REQUESTS", | ||
349 | gettext_noop ( | ||
350 | "set the maximum number of parallel requests for blocks that is allowed"), | ||
351 | &request_parallelism), | ||
352 | GNUNET_GETOPT_option_flag ('R', | ||
353 | "recursive", | ||
354 | gettext_noop ( | ||
355 | "download a GNUnet directory recursively"), | ||
356 | &do_recursive), | ||
357 | GNUNET_GETOPT_option_increment_uint ( | ||
358 | 'V', | ||
359 | "verbose", | ||
360 | gettext_noop ("be verbose (print progress information)"), | ||
361 | &verbose), | ||
362 | GNUNET_GETOPT_OPTION_END }; | ||
363 | |||
364 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
365 | return 2; | ||
366 | |||
367 | ret = | ||
368 | (GNUNET_OK == | ||
369 | GNUNET_PROGRAM_run ( | ||
370 | argc, | ||
371 | argv, | ||
372 | "gnunet-download [OPTIONS] URI", | ||
373 | gettext_noop ( | ||
374 | "Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"), | ||
375 | options, | ||
376 | &run, | ||
377 | NULL)) | ||
378 | ? ret | ||
379 | : 1; | ||
380 | GNUNET_free_nz ((void *) argv); | ||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | |||
385 | /* 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 | */ | ||
33 | static int ret; | ||
34 | |||
35 | /** | ||
36 | * Data file with the hosts for the testbed. | ||
37 | */ | ||
38 | static char *host_filename; | ||
39 | |||
40 | /** | ||
41 | * Number of peers to run in the experiment. | ||
42 | */ | ||
43 | static unsigned int num_peers; | ||
44 | |||
45 | /** | ||
46 | * After how long do we abort the test? | ||
47 | */ | ||
48 | static struct GNUNET_TIME_Relative timeout; | ||
49 | |||
50 | /** | ||
51 | * Handle to the task run during termination. | ||
52 | */ | ||
53 | static 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 | */ | ||
64 | static void | ||
65 | shutdown_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 | */ | ||
89 | static int | ||
90 | process_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 | */ | ||
113 | static void | ||
114 | terminate_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 | */ | ||
135 | static void | ||
136 | timeout_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 | */ | ||
155 | static void | ||
156 | test_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 | */ | ||
184 | static void | ||
185 | run (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 | */ | ||
203 | int | ||
204 | main (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 21e3c4a40..000000000 --- a/src/fs/gnunet-fs.c +++ /dev/null | |||
@@ -1,191 +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 | |||
27 | #include "gnunet_fs_service.h" | ||
28 | |||
29 | /** | ||
30 | * Return value. | ||
31 | */ | ||
32 | static int ret; | ||
33 | |||
34 | /** | ||
35 | * Handle to FS service. | ||
36 | */ | ||
37 | static struct GNUNET_FS_Handle *fs; | ||
38 | |||
39 | /** | ||
40 | * Handle for the index listing operation. | ||
41 | */ | ||
42 | static struct GNUNET_FS_GetIndexedContext *gic; | ||
43 | |||
44 | /** | ||
45 | * Option -i given? | ||
46 | */ | ||
47 | static int list_indexed_files; | ||
48 | |||
49 | /** | ||
50 | * Option -v given? | ||
51 | */ | ||
52 | static unsigned int verbose; | ||
53 | |||
54 | |||
55 | /** | ||
56 | * Print indexed filenames to stdout. | ||
57 | * | ||
58 | * @param cls closure | ||
59 | * @param filename the name of the file | ||
60 | * @param file_id hash of the contents of the indexed file | ||
61 | * @return #GNUNET_OK to continue iteration | ||
62 | */ | ||
63 | static enum GNUNET_GenericReturnValue | ||
64 | print_indexed (void *cls, | ||
65 | const char *filename, | ||
66 | const struct GNUNET_HashCode *file_id) | ||
67 | { | ||
68 | if (NULL == filename) | ||
69 | { | ||
70 | gic = NULL; | ||
71 | GNUNET_SCHEDULER_shutdown (); | ||
72 | return GNUNET_OK; | ||
73 | } | ||
74 | if (verbose) | ||
75 | fprintf (stdout, | ||
76 | "%s: %s\n", | ||
77 | GNUNET_h2s (file_id), | ||
78 | filename); | ||
79 | else | ||
80 | fprintf (stdout, | ||
81 | "%s\n", | ||
82 | filename); | ||
83 | return GNUNET_OK; | ||
84 | } | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Function run on shutdown. | ||
89 | * | ||
90 | * @param cls NULL | ||
91 | */ | ||
92 | static void | ||
93 | do_shutdown (void *cls) | ||
94 | { | ||
95 | (void) cls; | ||
96 | if (NULL != gic) | ||
97 | { | ||
98 | GNUNET_FS_get_indexed_files_cancel (gic); | ||
99 | gic = NULL; | ||
100 | } | ||
101 | if (NULL != fs) | ||
102 | { | ||
103 | GNUNET_FS_stop (fs); | ||
104 | fs = NULL; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | |||
109 | /** | ||
110 | * Main function that will be run by the scheduler. | ||
111 | * | ||
112 | * @param cls closure | ||
113 | * @param args remaining command-line arguments | ||
114 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
115 | * @param cfg configuration | ||
116 | */ | ||
117 | static void | ||
118 | run (void *cls, | ||
119 | char *const *args, | ||
120 | const char *cfgfile, | ||
121 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
122 | { | ||
123 | if (! list_indexed_files) | ||
124 | return; | ||
125 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, | ||
126 | NULL); | ||
127 | fs = GNUNET_FS_start (cfg, | ||
128 | "gnunet-fs", | ||
129 | NULL, | ||
130 | NULL, | ||
131 | GNUNET_FS_FLAGS_NONE, | ||
132 | GNUNET_FS_OPTIONS_END); | ||
133 | if (NULL == fs) | ||
134 | { | ||
135 | ret = 1; | ||
136 | return; | ||
137 | } | ||
138 | gic = GNUNET_FS_get_indexed_files (fs, | ||
139 | &print_indexed, | ||
140 | NULL); | ||
141 | if (NULL == gic) | ||
142 | { | ||
143 | ret = 2; | ||
144 | GNUNET_SCHEDULER_shutdown (); | ||
145 | return; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | |||
150 | /** | ||
151 | * The main function to access special file-sharing functions. | ||
152 | * | ||
153 | * @param argc number of arguments from the command line | ||
154 | * @param argv command line arguments | ||
155 | * @return 0 ok, 1 on error | ||
156 | */ | ||
157 | int | ||
158 | main (int argc, | ||
159 | char *const *argv) | ||
160 | { | ||
161 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
162 | GNUNET_GETOPT_option_flag ('i', | ||
163 | "list-indexed", | ||
164 | gettext_noop ( | ||
165 | "print a list of all indexed files"), | ||
166 | &list_indexed_files), | ||
167 | |||
168 | GNUNET_GETOPT_option_verbose (&verbose), | ||
169 | GNUNET_GETOPT_OPTION_END | ||
170 | }; | ||
171 | |||
172 | if (GNUNET_OK != | ||
173 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
174 | &argc, &argv)) | ||
175 | return 2; | ||
176 | ret = (GNUNET_OK == | ||
177 | GNUNET_PROGRAM_run (argc, | ||
178 | argv, | ||
179 | "gnunet-fs [OPTIONS]", | ||
180 | gettext_noop ("Special file-sharing operations"), | ||
181 | options, | ||
182 | &run, | ||
183 | NULL)) | ||
184 | ? ret | ||
185 | : 1; | ||
186 | GNUNET_free_nz ((void *) argv); | ||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | |||
191 | /* 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 0e07b79dc..000000000 --- a/src/fs/gnunet-helper-fs-publish.c +++ /dev/null | |||
@@ -1,579 +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 | |||
31 | #include "gnunet_fs_service.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * A node of a directory tree. | ||
36 | */ | ||
37 | struct ScanTreeNode | ||
38 | { | ||
39 | /** | ||
40 | * This is a doubly-linked list | ||
41 | */ | ||
42 | struct ScanTreeNode *next; | ||
43 | |||
44 | /** | ||
45 | * This is a doubly-linked list | ||
46 | */ | ||
47 | struct ScanTreeNode *prev; | ||
48 | |||
49 | /** | ||
50 | * Parent of this node, NULL for top-level entries. | ||
51 | */ | ||
52 | struct ScanTreeNode *parent; | ||
53 | |||
54 | /** | ||
55 | * This is a doubly-linked tree | ||
56 | * NULL for files and empty directories | ||
57 | */ | ||
58 | struct ScanTreeNode *children_head; | ||
59 | |||
60 | /** | ||
61 | * This is a doubly-linked tree | ||
62 | * NULL for files and empty directories | ||
63 | */ | ||
64 | struct ScanTreeNode *children_tail; | ||
65 | |||
66 | /** | ||
67 | * Name of the file/directory | ||
68 | */ | ||
69 | char *filename; | ||
70 | |||
71 | /** | ||
72 | * Size of the file (if it is a file), in bytes. | ||
73 | * At the moment it is set to 0 for directories. | ||
74 | */ | ||
75 | uint64_t file_size; | ||
76 | |||
77 | /** | ||
78 | * #GNUNET_YES if this is a directory | ||
79 | */ | ||
80 | int is_directory; | ||
81 | }; | ||
82 | |||
83 | |||
84 | #if HAVE_LIBEXTRACTOR | ||
85 | /** | ||
86 | * List of libextractor plugins to use for extracting. | ||
87 | */ | ||
88 | static struct EXTRACTOR_PluginList *plugins; | ||
89 | #endif | ||
90 | |||
91 | /** | ||
92 | * File descriptor we use for IPC with the parent. | ||
93 | */ | ||
94 | static int output_stream; | ||
95 | |||
96 | |||
97 | #if HAVE_LIBEXTRACTOR | ||
98 | /** | ||
99 | * Add meta data that libextractor finds to our meta data | ||
100 | * container. | ||
101 | * | ||
102 | * @param cls closure, our meta data container | ||
103 | * @param plugin_name name of the plugin that produced this value; | ||
104 | * special values can be used (e.g. '<zlib>' for zlib being | ||
105 | * used in the main libextractor library and yielding | ||
106 | * meta data). | ||
107 | * @param type libextractor-type describing the meta data | ||
108 | * @param format basic format information about data | ||
109 | * @param data_mime_type mime-type of data (not of the original file); | ||
110 | * can be NULL (if mime-type is not known) | ||
111 | * @param data actual meta-data found | ||
112 | * @param data_len number of bytes in @a data | ||
113 | * @return always 0 to continue extracting | ||
114 | */ | ||
115 | static int | ||
116 | add_to_md (void *cls, | ||
117 | const char *plugin_name, | ||
118 | enum EXTRACTOR_MetaType type, | ||
119 | enum EXTRACTOR_MetaFormat format, | ||
120 | const char *data_mime_type, | ||
121 | const char *data, | ||
122 | size_t data_len) | ||
123 | { | ||
124 | struct GNUNET_FS_MetaData *md = cls; | ||
125 | |||
126 | if (((EXTRACTOR_METAFORMAT_UTF8 == format) || | ||
127 | (EXTRACTOR_METAFORMAT_C_STRING == format)) && | ||
128 | ('\0' != data[data_len - 1])) | ||
129 | { | ||
130 | char zdata[data_len + 1]; | ||
131 | GNUNET_memcpy (zdata, data, data_len); | ||
132 | zdata[data_len] = '\0'; | ||
133 | (void) GNUNET_FS_meta_data_insert (md, | ||
134 | plugin_name, | ||
135 | type, | ||
136 | format, | ||
137 | data_mime_type, | ||
138 | zdata, | ||
139 | data_len + 1); | ||
140 | } | ||
141 | else | ||
142 | { | ||
143 | (void) GNUNET_FS_meta_data_insert (md, | ||
144 | plugin_name, | ||
145 | type, | ||
146 | format, | ||
147 | data_mime_type, | ||
148 | data, | ||
149 | data_len); | ||
150 | } | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | |||
155 | #endif | ||
156 | |||
157 | |||
158 | /** | ||
159 | * Free memory of the @a tree structure | ||
160 | * | ||
161 | * @param tree tree to free | ||
162 | */ | ||
163 | static void | ||
164 | free_tree (struct ScanTreeNode *tree) | ||
165 | { | ||
166 | struct ScanTreeNode *pos; | ||
167 | |||
168 | while (NULL != (pos = tree->children_head)) | ||
169 | free_tree (pos); | ||
170 | if (NULL != tree->parent) | ||
171 | GNUNET_CONTAINER_DLL_remove (tree->parent->children_head, | ||
172 | tree->parent->children_tail, | ||
173 | tree); | ||
174 | GNUNET_free (tree->filename); | ||
175 | GNUNET_free (tree); | ||
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * Write @a size bytes from @a buf into the #output_stream. | ||
181 | * | ||
182 | * @param buf buffer with data to write | ||
183 | * @param size number of bytes to write | ||
184 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
185 | */ | ||
186 | static int | ||
187 | write_all (const void *buf, size_t size) | ||
188 | { | ||
189 | const char *cbuf = buf; | ||
190 | size_t total; | ||
191 | ssize_t wr; | ||
192 | |||
193 | total = 0; | ||
194 | do | ||
195 | { | ||
196 | wr = write (output_stream, &cbuf[total], size - total); | ||
197 | if (wr > 0) | ||
198 | total += wr; | ||
199 | } | ||
200 | while ((wr > 0) && (total < size)); | ||
201 | if (wr <= 0) | ||
202 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
203 | "Failed to write to stdout: %s\n", | ||
204 | strerror (errno)); | ||
205 | return (total == size) ? GNUNET_OK : GNUNET_SYSERR; | ||
206 | } | ||
207 | |||
208 | |||
209 | /** | ||
210 | * Write message to the master process. | ||
211 | * | ||
212 | * @param message_type message type to use | ||
213 | * @param data data to append, NULL for none | ||
214 | * @param data_length number of bytes in @a data | ||
215 | * @return #GNUNET_SYSERR to stop scanning (the pipe was broken somehow) | ||
216 | */ | ||
217 | static int | ||
218 | write_message (uint16_t message_type, const char *data, size_t data_length) | ||
219 | { | ||
220 | struct GNUNET_MessageHeader hdr; | ||
221 | |||
222 | #if 0 | ||
223 | fprintf (stderr, | ||
224 | "Helper sends %u-byte message of type %u\n", | ||
225 | (unsigned int) (sizeof(struct GNUNET_MessageHeader) + data_length), | ||
226 | (unsigned int) message_type); | ||
227 | #endif | ||
228 | hdr.type = htons (message_type); | ||
229 | hdr.size = htons (sizeof(struct GNUNET_MessageHeader) + data_length); | ||
230 | if ((GNUNET_OK != write_all (&hdr, sizeof(hdr))) || | ||
231 | (GNUNET_OK != write_all (data, data_length))) | ||
232 | return GNUNET_SYSERR; | ||
233 | return GNUNET_OK; | ||
234 | } | ||
235 | |||
236 | |||
237 | /** | ||
238 | * Function called to (recursively) add all of the files in the | ||
239 | * directory to the tree. Called by the directory scanner to initiate | ||
240 | * the scan. Does NOT yet add any metadata. | ||
241 | * | ||
242 | * @param filename file or directory to scan | ||
243 | * @param dst where to store the resulting share tree item; | ||
244 | * NULL is stored in @a dst upon recoverable errors (#GNUNET_OK is returned) | ||
245 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
246 | */ | ||
247 | static int | ||
248 | preprocess_file (const char *filename, struct ScanTreeNode **dst); | ||
249 | |||
250 | |||
251 | /** | ||
252 | * Closure for the 'scan_callback' | ||
253 | */ | ||
254 | struct RecursionContext | ||
255 | { | ||
256 | /** | ||
257 | * Parent to add the files to. | ||
258 | */ | ||
259 | struct ScanTreeNode *parent; | ||
260 | |||
261 | /** | ||
262 | * Flag to set to GNUNET_YES on serious errors. | ||
263 | */ | ||
264 | int stop; | ||
265 | }; | ||
266 | |||
267 | |||
268 | /** | ||
269 | * Function called by the directory iterator to (recursively) add all | ||
270 | * of the files in the directory to the tree. Called by the directory | ||
271 | * scanner to initiate the scan. Does NOT yet add any metadata. | ||
272 | * | ||
273 | * @param cls the `struct RecursionContext` | ||
274 | * @param filename file or directory to scan | ||
275 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
276 | */ | ||
277 | static int | ||
278 | scan_callback (void *cls, const char *filename) | ||
279 | { | ||
280 | struct RecursionContext *rc = cls; | ||
281 | struct ScanTreeNode *chld; | ||
282 | |||
283 | if (GNUNET_OK != preprocess_file (filename, &chld)) | ||
284 | { | ||
285 | rc->stop = GNUNET_YES; | ||
286 | return GNUNET_SYSERR; | ||
287 | } | ||
288 | if (NULL == chld) | ||
289 | return GNUNET_OK; | ||
290 | chld->parent = rc->parent; | ||
291 | GNUNET_CONTAINER_DLL_insert (rc->parent->children_head, | ||
292 | rc->parent->children_tail, | ||
293 | chld); | ||
294 | return GNUNET_OK; | ||
295 | } | ||
296 | |||
297 | |||
298 | /** | ||
299 | * Function called to (recursively) add all of the files in the | ||
300 | * directory to the tree. Called by the directory scanner to initiate | ||
301 | * the scan. Does NOT yet add any metadata. | ||
302 | * | ||
303 | * @param filename file or directory to scan | ||
304 | * @param dst where to store the resulting share tree item; | ||
305 | * NULL is stored in @a dst upon recoverable errors (#GNUNET_OK is returned) | ||
306 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
307 | */ | ||
308 | static int | ||
309 | preprocess_file (const char *filename, struct ScanTreeNode **dst) | ||
310 | { | ||
311 | struct ScanTreeNode *item; | ||
312 | struct stat sbuf; | ||
313 | uint64_t fsize = 0; | ||
314 | |||
315 | if ((0 != stat (filename, &sbuf)) || | ||
316 | ((! S_ISDIR (sbuf.st_mode)) && | ||
317 | (GNUNET_OK != | ||
318 | GNUNET_DISK_file_size (filename, &fsize, GNUNET_NO, GNUNET_YES)))) | ||
319 | { | ||
320 | /* If the file doesn't exist (or is not stat-able for any other reason) | ||
321 | skip it (but report it), but do continue. */ | ||
322 | if (GNUNET_OK != | ||
323 | write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_SKIP_FILE, | ||
324 | filename, | ||
325 | strlen (filename) + 1)) | ||
326 | return GNUNET_SYSERR; | ||
327 | /* recoverable error, store 'NULL' in *dst */ | ||
328 | *dst = NULL; | ||
329 | return GNUNET_OK; | ||
330 | } | ||
331 | |||
332 | /* Report the progress */ | ||
333 | if ( | ||
334 | GNUNET_OK != | ||
335 | write_message (S_ISDIR (sbuf.st_mode) | ||
336 | ? GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_DIRECTORY | ||
337 | : GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_FILE, | ||
338 | filename, | ||
339 | strlen (filename) + 1)) | ||
340 | return GNUNET_SYSERR; | ||
341 | item = GNUNET_new (struct ScanTreeNode); | ||
342 | item->filename = GNUNET_strdup (filename); | ||
343 | item->is_directory = (S_ISDIR (sbuf.st_mode)) ? GNUNET_YES : GNUNET_NO; | ||
344 | item->file_size = fsize; | ||
345 | if (GNUNET_YES == item->is_directory) | ||
346 | { | ||
347 | struct RecursionContext rc; | ||
348 | |||
349 | rc.parent = item; | ||
350 | rc.stop = GNUNET_NO; | ||
351 | GNUNET_DISK_directory_scan (filename, &scan_callback, &rc); | ||
352 | if ( | ||
353 | (GNUNET_YES == rc.stop) || | ||
354 | (GNUNET_OK != | ||
355 | write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_DIRECTORY, | ||
356 | "..", | ||
357 | 3))) | ||
358 | { | ||
359 | free_tree (item); | ||
360 | return GNUNET_SYSERR; | ||
361 | } | ||
362 | } | ||
363 | *dst = item; | ||
364 | return GNUNET_OK; | ||
365 | } | ||
366 | |||
367 | |||
368 | /** | ||
369 | * Extract metadata from files. | ||
370 | * | ||
371 | * @param item entry we are processing | ||
372 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on fatal errors | ||
373 | */ | ||
374 | static int | ||
375 | extract_files (struct ScanTreeNode *item) | ||
376 | { | ||
377 | struct GNUNET_FS_MetaData *meta; | ||
378 | ssize_t size; | ||
379 | size_t slen; | ||
380 | |||
381 | if (GNUNET_YES == item->is_directory) | ||
382 | { | ||
383 | /* for directories, we simply only descent, no extraction, no | ||
384 | progress reporting */ | ||
385 | struct ScanTreeNode *pos; | ||
386 | |||
387 | for (pos = item->children_head; NULL != pos; pos = pos->next) | ||
388 | if (GNUNET_OK != extract_files (pos)) | ||
389 | return GNUNET_SYSERR; | ||
390 | return GNUNET_OK; | ||
391 | } | ||
392 | |||
393 | /* this is the expensive operation, *afterwards* we'll check for aborts */ | ||
394 | meta = GNUNET_FS_meta_data_create (); | ||
395 | #if HAVE_LIBEXTRACTOR | ||
396 | EXTRACTOR_extract (plugins, item->filename, NULL, 0, &add_to_md, meta); | ||
397 | #endif | ||
398 | slen = strlen (item->filename) + 1; | ||
399 | size = GNUNET_FS_meta_data_get_serialized_size (meta); | ||
400 | if (-1 == size) | ||
401 | { | ||
402 | /* no meta data */ | ||
403 | GNUNET_FS_meta_data_destroy (meta); | ||
404 | if (GNUNET_OK != | ||
405 | write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_META_DATA, | ||
406 | item->filename, | ||
407 | slen)) | ||
408 | return GNUNET_SYSERR; | ||
409 | return GNUNET_OK; | ||
410 | } | ||
411 | else if (size > (UINT16_MAX - sizeof(struct GNUNET_MessageHeader) - slen)) | ||
412 | { | ||
413 | /* We can't transfer more than 64k bytes in one message. */ | ||
414 | size = UINT16_MAX - sizeof(struct GNUNET_MessageHeader) - slen; | ||
415 | } | ||
416 | { | ||
417 | char buf[size + slen]; | ||
418 | char *dst = &buf[slen]; | ||
419 | |||
420 | GNUNET_memcpy (buf, item->filename, slen); | ||
421 | size = GNUNET_FS_meta_data_serialize ( | ||
422 | meta, | ||
423 | &dst, | ||
424 | size, | ||
425 | GNUNET_FS_META_DATA_SERIALIZE_PART); | ||
426 | if (size < 0) | ||
427 | { | ||
428 | GNUNET_break (0); | ||
429 | size = 0; | ||
430 | } | ||
431 | GNUNET_FS_meta_data_destroy (meta); | ||
432 | if (GNUNET_OK != | ||
433 | write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_META_DATA, | ||
434 | buf, | ||
435 | slen + size)) | ||
436 | return GNUNET_SYSERR; | ||
437 | } | ||
438 | return GNUNET_OK; | ||
439 | } | ||
440 | |||
441 | |||
442 | /** | ||
443 | * Install a signal handler to ignore SIGPIPE. | ||
444 | */ | ||
445 | static void | ||
446 | ignore_sigpipe () | ||
447 | { | ||
448 | struct sigaction oldsig; | ||
449 | struct sigaction sig; | ||
450 | |||
451 | memset (&sig, 0, sizeof(struct sigaction)); | ||
452 | sig.sa_handler = SIG_IGN; | ||
453 | sigemptyset (&sig.sa_mask); | ||
454 | #ifdef SA_INTERRUPT | ||
455 | sig.sa_flags = SA_INTERRUPT; /* SunOS */ | ||
456 | #else | ||
457 | sig.sa_flags = SA_RESTART; | ||
458 | #endif | ||
459 | if (0 != sigaction (SIGPIPE, &sig, &oldsig)) | ||
460 | fprintf (stderr, | ||
461 | "Failed to install SIGPIPE handler: %s\n", | ||
462 | strerror (errno)); | ||
463 | } | ||
464 | |||
465 | |||
466 | /** | ||
467 | * Turn the given file descriptor in to '/dev/null'. | ||
468 | * | ||
469 | * @param fd fd to bind to /dev/null | ||
470 | * @param flags flags to use (O_RDONLY or O_WRONLY) | ||
471 | */ | ||
472 | static void | ||
473 | make_dev_zero (int fd, int flags) | ||
474 | { | ||
475 | int z; | ||
476 | |||
477 | GNUNET_assert (0 == close (fd)); | ||
478 | z = open ("/dev/null", flags); | ||
479 | GNUNET_assert (-1 != z); | ||
480 | if (z == fd) | ||
481 | return; | ||
482 | GNUNET_break (fd == dup2 (z, fd)); | ||
483 | GNUNET_assert (0 == close (z)); | ||
484 | } | ||
485 | |||
486 | |||
487 | /** | ||
488 | * Main function of the helper process to extract meta data. | ||
489 | * | ||
490 | * @param argc should be 3 | ||
491 | * @param argv [0] our binary name | ||
492 | * [1] name of the file or directory to process | ||
493 | * [2] "-" to disable extraction, NULL for defaults, | ||
494 | * otherwise custom plugins to load from LE | ||
495 | * @return 0 on success | ||
496 | */ | ||
497 | int | ||
498 | main (int argc, char *const *argv) | ||
499 | { | ||
500 | const char *filename_expanded; | ||
501 | const char *ex; | ||
502 | struct ScanTreeNode *root; | ||
503 | |||
504 | ignore_sigpipe (); | ||
505 | /* move stdout to some other FD for IPC, bind | ||
506 | stdout/stderr to /dev/null */ | ||
507 | output_stream = dup (1); | ||
508 | make_dev_zero (1, O_WRONLY); | ||
509 | make_dev_zero (2, O_WRONLY); | ||
510 | |||
511 | /* parse command line */ | ||
512 | if ((3 != argc) && (2 != argc)) | ||
513 | { | ||
514 | fprintf (stderr, | ||
515 | "%s", | ||
516 | "gnunet-helper-fs-publish needs exactly one or two arguments\n"); | ||
517 | return 1; | ||
518 | } | ||
519 | filename_expanded = argv[1]; | ||
520 | ex = argv[2]; | ||
521 | if ((NULL == ex) || (0 != strcmp (ex, "-"))) | ||
522 | { | ||
523 | #if HAVE_LIBEXTRACTOR | ||
524 | plugins = EXTRACTOR_plugin_add_defaults (EXTRACTOR_OPTION_DEFAULT_POLICY); | ||
525 | if (NULL != ex) | ||
526 | plugins = EXTRACTOR_plugin_add_config (plugins, | ||
527 | ex, | ||
528 | EXTRACTOR_OPTION_DEFAULT_POLICY); | ||
529 | #endif | ||
530 | } | ||
531 | |||
532 | /* scan tree to find out how much work there is to be done */ | ||
533 | if (GNUNET_OK != preprocess_file (filename_expanded, &root)) | ||
534 | { | ||
535 | (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR, NULL, 0); | ||
536 | #if HAVE_LIBEXTRACTOR | ||
537 | EXTRACTOR_plugin_remove_all (plugins); | ||
538 | #endif | ||
539 | return 2; | ||
540 | } | ||
541 | /* signal that we're done counting files, so that a percentage of | ||
542 | progress can now be calculated */ | ||
543 | if (GNUNET_OK != | ||
544 | write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_COUNTING_DONE, | ||
545 | NULL, | ||
546 | 0)) | ||
547 | { | ||
548 | #if HAVE_LIBEXTRACTOR | ||
549 | EXTRACTOR_plugin_remove_all (plugins); | ||
550 | #endif | ||
551 | return 3; | ||
552 | } | ||
553 | if (NULL != root) | ||
554 | { | ||
555 | if (GNUNET_OK != extract_files (root)) | ||
556 | { | ||
557 | (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR, | ||
558 | NULL, | ||
559 | 0); | ||
560 | free_tree (root); | ||
561 | #if HAVE_LIBEXTRACTOR | ||
562 | EXTRACTOR_plugin_remove_all (plugins); | ||
563 | #endif | ||
564 | return 4; | ||
565 | } | ||
566 | free_tree (root); | ||
567 | } | ||
568 | /* enable "clean" shutdown by telling parent that we are done */ | ||
569 | (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_FINISHED, | ||
570 | NULL, | ||
571 | 0); | ||
572 | #if HAVE_LIBEXTRACTOR | ||
573 | EXTRACTOR_plugin_remove_all (plugins); | ||
574 | #endif | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | |||
579 | /* 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 7a87130de..000000000 --- a/src/fs/gnunet-publish.c +++ /dev/null | |||
@@ -1,1009 +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 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | #include "gnunet_identity_service.h" | ||
32 | |||
33 | /** | ||
34 | * Global return value from #main(). | ||
35 | */ | ||
36 | static int ret; | ||
37 | |||
38 | /** | ||
39 | * Command line option 'verbose' set | ||
40 | */ | ||
41 | static unsigned int verbose; | ||
42 | |||
43 | /** | ||
44 | * Handle to our configuration. | ||
45 | */ | ||
46 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
47 | |||
48 | /** | ||
49 | * Handle for interaction with file-sharing service. | ||
50 | */ | ||
51 | static struct GNUNET_FS_Handle *ctx; | ||
52 | |||
53 | /** | ||
54 | * Handle to FS-publishing operation. | ||
55 | */ | ||
56 | static struct GNUNET_FS_PublishContext *pc; | ||
57 | |||
58 | /** | ||
59 | * Meta-data provided via command-line option. | ||
60 | */ | ||
61 | static struct GNUNET_FS_MetaData *meta; | ||
62 | |||
63 | /** | ||
64 | * Keywords provided via command-line option. | ||
65 | */ | ||
66 | static struct GNUNET_FS_Uri *topKeywords; | ||
67 | |||
68 | /** | ||
69 | * Options we set for published blocks. | ||
70 | */ | ||
71 | static struct GNUNET_FS_BlockOptions bo = { { 0LL }, 1, 365, 1 }; | ||
72 | |||
73 | /** | ||
74 | * Value of URI provided on command-line (when not publishing | ||
75 | * a file but just creating UBlocks to refer to an existing URI). | ||
76 | */ | ||
77 | static char *uri_string; | ||
78 | |||
79 | /** | ||
80 | * Value of URI provided on command-line (when not publishing | ||
81 | * a file but just creating UBlocks to refer to an existing URI); | ||
82 | * parsed version of 'uri_string'. | ||
83 | */ | ||
84 | static struct GNUNET_FS_Uri *uri; | ||
85 | |||
86 | /** | ||
87 | * Command-line option for namespace publishing: identifier for updates | ||
88 | * to this publication. | ||
89 | */ | ||
90 | static char *next_id; | ||
91 | |||
92 | /** | ||
93 | * Command-line option for namespace publishing: identifier for this | ||
94 | * publication. | ||
95 | */ | ||
96 | static char *this_id; | ||
97 | |||
98 | /** | ||
99 | * Command-line option identifying the pseudonym to use for the publication. | ||
100 | */ | ||
101 | static char *pseudonym; | ||
102 | |||
103 | /** | ||
104 | * Command-line option for 'inserting' | ||
105 | */ | ||
106 | static int do_insert; | ||
107 | |||
108 | /** | ||
109 | * Command-line option to disable meta data extraction. | ||
110 | */ | ||
111 | static int disable_extractor; | ||
112 | |||
113 | /** | ||
114 | * Command-line option to merely simulate publishing operation. | ||
115 | */ | ||
116 | static int do_simulate; | ||
117 | |||
118 | /** | ||
119 | * Command-line option to only perform meta data extraction, but not publish. | ||
120 | */ | ||
121 | static int extract_only; | ||
122 | |||
123 | /** | ||
124 | * Command-line option to disable adding creation time. | ||
125 | */ | ||
126 | static int enable_creation_time; | ||
127 | |||
128 | /** | ||
129 | * Handle to the directory scanner (for recursive insertions). | ||
130 | */ | ||
131 | static struct GNUNET_FS_DirScanner *ds; | ||
132 | |||
133 | /** | ||
134 | * Which namespace do we publish to? NULL if we do not publish to | ||
135 | * a namespace. | ||
136 | */ | ||
137 | static struct GNUNET_IDENTITY_Ego *namespace; | ||
138 | |||
139 | /** | ||
140 | * Handle to identity service. | ||
141 | */ | ||
142 | static struct GNUNET_IDENTITY_Handle *identity; | ||
143 | |||
144 | |||
145 | /** | ||
146 | * We are finished with the publishing operation, clean up all | ||
147 | * FS state. | ||
148 | * | ||
149 | * @param cls NULL | ||
150 | */ | ||
151 | static void | ||
152 | do_stop_task (void *cls) | ||
153 | { | ||
154 | struct GNUNET_FS_PublishContext *p; | ||
155 | |||
156 | if (NULL != ds) | ||
157 | { | ||
158 | GNUNET_FS_directory_scan_abort (ds); | ||
159 | ds = NULL; | ||
160 | } | ||
161 | if (NULL != identity) | ||
162 | { | ||
163 | GNUNET_IDENTITY_disconnect (identity); | ||
164 | identity = NULL; | ||
165 | } | ||
166 | if (NULL != pc) | ||
167 | { | ||
168 | p = pc; | ||
169 | pc = NULL; | ||
170 | GNUNET_FS_publish_stop (p); | ||
171 | } | ||
172 | if (NULL != ctx) | ||
173 | { | ||
174 | GNUNET_FS_stop (ctx); | ||
175 | ctx = NULL; | ||
176 | } | ||
177 | if (NULL != meta) | ||
178 | { | ||
179 | GNUNET_FS_meta_data_destroy (meta); | ||
180 | meta = NULL; | ||
181 | } | ||
182 | if (NULL != uri) | ||
183 | { | ||
184 | GNUNET_FS_uri_destroy (uri); | ||
185 | uri = NULL; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | |||
190 | /** | ||
191 | * Called by FS client to give information about the progress of an | ||
192 | * operation. | ||
193 | * | ||
194 | * @param cls closure | ||
195 | * @param info details about the event, specifying the event type | ||
196 | * and various bits about the event | ||
197 | * @return client-context (for the next progress call | ||
198 | * for this operation; should be set to NULL for | ||
199 | * SUSPEND and STOPPED events). The value returned | ||
200 | * will be passed to future callbacks in the respective | ||
201 | * field in the GNUNET_FS_ProgressInfo struct. | ||
202 | */ | ||
203 | static void * | ||
204 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) | ||
205 | { | ||
206 | const char *s; | ||
207 | char *suri; | ||
208 | |||
209 | switch (info->status) | ||
210 | { | ||
211 | case GNUNET_FS_STATUS_PUBLISH_START: | ||
212 | break; | ||
213 | |||
214 | case GNUNET_FS_STATUS_PUBLISH_PROGRESS: | ||
215 | if (verbose) | ||
216 | { | ||
217 | s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.eta, | ||
218 | GNUNET_YES); | ||
219 | fprintf (stdout, | ||
220 | _ ("Publishing `%s' at %llu/%llu (%s remaining)\n"), | ||
221 | info->value.publish.filename, | ||
222 | (unsigned long long) info->value.publish.completed, | ||
223 | (unsigned long long) info->value.publish.size, | ||
224 | s); | ||
225 | } | ||
226 | break; | ||
227 | |||
228 | case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY: | ||
229 | if (verbose) | ||
230 | { | ||
231 | s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.specifics | ||
232 | .progress_directory.eta, | ||
233 | GNUNET_YES); | ||
234 | fprintf (stdout, | ||
235 | _ ("Publishing `%s' at %llu/%llu (%s remaining)\n"), | ||
236 | info->value.publish.filename, | ||
237 | (unsigned long long) | ||
238 | info->value.publish.specifics.progress_directory.completed, | ||
239 | (unsigned long long) | ||
240 | info->value.publish.specifics.progress_directory.total, | ||
241 | s); | ||
242 | } | ||
243 | break; | ||
244 | |||
245 | case GNUNET_FS_STATUS_PUBLISH_ERROR: | ||
246 | fprintf (stderr, | ||
247 | _ ("Error publishing: %s.\n"), | ||
248 | info->value.publish.specifics.error.message); | ||
249 | ret = 1; | ||
250 | GNUNET_SCHEDULER_shutdown (); | ||
251 | break; | ||
252 | |||
253 | case GNUNET_FS_STATUS_PUBLISH_COMPLETED: | ||
254 | fprintf (stdout, | ||
255 | _ ("Publishing `%s' done.\n"), | ||
256 | info->value.publish.filename); | ||
257 | suri = | ||
258 | GNUNET_FS_uri_to_string (info->value.publish.specifics.completed.chk_uri); | ||
259 | fprintf (stdout, _ ("URI is `%s'.\n"), suri); | ||
260 | GNUNET_free (suri); | ||
261 | if (NULL != info->value.publish.specifics.completed.sks_uri) | ||
262 | { | ||
263 | suri = GNUNET_FS_uri_to_string ( | ||
264 | info->value.publish.specifics.completed.sks_uri); | ||
265 | fprintf (stdout, _ ("Namespace URI is `%s'.\n"), suri); | ||
266 | GNUNET_free (suri); | ||
267 | } | ||
268 | if (NULL == info->value.publish.pctx) | ||
269 | { | ||
270 | ret = 0; | ||
271 | GNUNET_SCHEDULER_shutdown (); | ||
272 | } | ||
273 | break; | ||
274 | |||
275 | case GNUNET_FS_STATUS_PUBLISH_STOPPED: | ||
276 | GNUNET_break (NULL == pc); | ||
277 | return NULL; | ||
278 | |||
279 | case GNUNET_FS_STATUS_UNINDEX_START: | ||
280 | fprintf (stderr, "%s", _ ("Starting cleanup after abort\n")); | ||
281 | return NULL; | ||
282 | |||
283 | case GNUNET_FS_STATUS_UNINDEX_PROGRESS: | ||
284 | return NULL; | ||
285 | |||
286 | case GNUNET_FS_STATUS_UNINDEX_COMPLETED: | ||
287 | fprintf (stderr, "%s", _ ("Cleanup after abort completed.\n")); | ||
288 | GNUNET_FS_unindex_stop (info->value.unindex.uc); | ||
289 | return NULL; | ||
290 | |||
291 | case GNUNET_FS_STATUS_UNINDEX_ERROR: | ||
292 | fprintf (stderr, "%s", _ ("Cleanup after abort failed.\n")); | ||
293 | GNUNET_FS_unindex_stop (info->value.unindex.uc); | ||
294 | return NULL; | ||
295 | |||
296 | case GNUNET_FS_STATUS_UNINDEX_STOPPED: | ||
297 | return NULL; | ||
298 | |||
299 | default: | ||
300 | fprintf (stderr, _ ("Unexpected status: %d\n"), info->status); | ||
301 | return NULL; | ||
302 | } | ||
303 | return ""; /* non-null */ | ||
304 | } | ||
305 | |||
306 | |||
307 | /** | ||
308 | * Print metadata entries (except binary | ||
309 | * metadata and the filename). | ||
310 | * | ||
311 | * @param cls closure | ||
312 | * @param plugin_name name of the plugin that generated the meta data | ||
313 | * @param type type of the meta data | ||
314 | * @param format format of data | ||
315 | * @param data_mime_type mime type of @a data | ||
316 | * @param data value of the meta data | ||
317 | * @param data_size number of bytes in @a data | ||
318 | * @return always 0 | ||
319 | */ | ||
320 | static int | ||
321 | meta_printer (void *cls, | ||
322 | const char *plugin_name, | ||
323 | enum EXTRACTOR_MetaType type, | ||
324 | enum EXTRACTOR_MetaFormat format, | ||
325 | const char *data_mime_type, | ||
326 | const char *data, | ||
327 | size_t data_size) | ||
328 | { | ||
329 | if ((EXTRACTOR_METAFORMAT_UTF8 != format) && | ||
330 | (EXTRACTOR_METAFORMAT_C_STRING != format)) | ||
331 | return 0; | ||
332 | if (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type) | ||
333 | return 0; | ||
334 | #if HAVE_LIBEXTRACTOR | ||
335 | fprintf (stdout, "\t%s - %s\n", EXTRACTOR_metatype_to_string (type), data); | ||
336 | #else | ||
337 | fprintf (stdout, "\t%d - %s\n", type, data); | ||
338 | #endif | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | |||
343 | /** | ||
344 | * Iterator printing keywords | ||
345 | * | ||
346 | * @param cls closure | ||
347 | * @param keyword the keyword | ||
348 | * @param is_mandatory is the keyword mandatory (in a search) | ||
349 | * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to abort | ||
350 | */ | ||
351 | static int | ||
352 | keyword_printer (void *cls, const char *keyword, int is_mandatory) | ||
353 | { | ||
354 | fprintf (stdout, "\t%s\n", keyword); | ||
355 | return GNUNET_OK; | ||
356 | } | ||
357 | |||
358 | |||
359 | /** | ||
360 | * Function called on all entries before the publication. This is | ||
361 | * where we perform modifications to the default based on command-line | ||
362 | * options. | ||
363 | * | ||
364 | * @param cls closure | ||
365 | * @param fi the entry in the publish-structure | ||
366 | * @param length length of the file or directory | ||
367 | * @param m metadata for the file or directory (can be modified) | ||
368 | * @param uri pointer to the keywords that will be used for this entry (can be modified) | ||
369 | * @param bo block options | ||
370 | * @param do_index should we index? | ||
371 | * @param client_info pointer to client context set upon creation (can be modified) | ||
372 | * @return #GNUNET_OK to continue, #GNUNET_NO to remove | ||
373 | * this entry from the directory, #GNUNET_SYSERR | ||
374 | * to abort the iteration | ||
375 | */ | ||
376 | static int | ||
377 | publish_inspector (void *cls, | ||
378 | struct GNUNET_FS_FileInformation *fi, | ||
379 | uint64_t length, | ||
380 | struct GNUNET_FS_MetaData *m, | ||
381 | struct GNUNET_FS_Uri **uri, | ||
382 | struct GNUNET_FS_BlockOptions *bo, | ||
383 | int *do_index, | ||
384 | void **client_info) | ||
385 | { | ||
386 | char *fn; | ||
387 | char *fs; | ||
388 | struct GNUNET_FS_Uri *new_uri; | ||
389 | |||
390 | if (cls == fi) | ||
391 | return GNUNET_OK; | ||
392 | if ((disable_extractor) && (NULL != *uri)) | ||
393 | { | ||
394 | GNUNET_FS_uri_destroy (*uri); | ||
395 | *uri = NULL; | ||
396 | } | ||
397 | if (NULL != topKeywords) | ||
398 | { | ||
399 | if (NULL != *uri) | ||
400 | { | ||
401 | new_uri = GNUNET_FS_uri_ksk_merge (topKeywords, *uri); | ||
402 | GNUNET_FS_uri_destroy (*uri); | ||
403 | *uri = new_uri; | ||
404 | GNUNET_FS_uri_destroy (topKeywords); | ||
405 | } | ||
406 | else | ||
407 | { | ||
408 | *uri = topKeywords; | ||
409 | } | ||
410 | topKeywords = NULL; | ||
411 | } | ||
412 | if (NULL != meta) | ||
413 | { | ||
414 | GNUNET_FS_meta_data_merge (m, meta); | ||
415 | GNUNET_FS_meta_data_destroy (meta); | ||
416 | meta = NULL; | ||
417 | } | ||
418 | if (enable_creation_time) | ||
419 | GNUNET_FS_meta_data_add_publication_date (m); | ||
420 | if (extract_only) | ||
421 | { | ||
422 | fn = GNUNET_FS_meta_data_get_by_type ( | ||
423 | m, | ||
424 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); | ||
425 | fs = GNUNET_STRINGS_byte_size_fancy (length); | ||
426 | fprintf (stdout, _ ("Meta data for file `%s' (%s)\n"), fn, fs); | ||
427 | GNUNET_FS_meta_data_iterate (m, &meta_printer, NULL); | ||
428 | fprintf (stdout, _ ("Keywords for file `%s' (%s)\n"), fn, fs); | ||
429 | GNUNET_free (fn); | ||
430 | GNUNET_free (fs); | ||
431 | if (NULL != *uri) | ||
432 | GNUNET_FS_uri_ksk_get_keywords (*uri, &keyword_printer, NULL); | ||
433 | fprintf (stdout, "%s", "\n"); | ||
434 | } | ||
435 | if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (m)) | ||
436 | GNUNET_FS_file_information_inspect (fi, &publish_inspector, fi); | ||
437 | return GNUNET_OK; | ||
438 | } | ||
439 | |||
440 | |||
441 | /** | ||
442 | * Function called upon completion of the publishing | ||
443 | * of the UBLOCK for the SKS URI. As this is the last | ||
444 | * step, stop our interaction with FS (clean up). | ||
445 | * | ||
446 | * @param cls NULL (closure) | ||
447 | * @param sks_uri URI for the block that was published | ||
448 | * @param emsg error message, NULL on success | ||
449 | */ | ||
450 | static void | ||
451 | uri_sks_continuation (void *cls, | ||
452 | const struct GNUNET_FS_Uri *sks_uri, | ||
453 | const char *emsg) | ||
454 | { | ||
455 | if (NULL != emsg) | ||
456 | { | ||
457 | fprintf (stderr, "%s\n", emsg); | ||
458 | ret = 1; | ||
459 | } | ||
460 | GNUNET_SCHEDULER_shutdown (); | ||
461 | } | ||
462 | |||
463 | |||
464 | /** | ||
465 | * Function called upon completion of the publishing | ||
466 | * of the UBLOCK for the KSK URI. Continue with | ||
467 | * publishing the SKS URI (if applicable) or clean up. | ||
468 | * | ||
469 | * @param cls NULL (closure) | ||
470 | * @param ksk_uri URI for the block that was published | ||
471 | * @param emsg error message, NULL on success | ||
472 | */ | ||
473 | static void | ||
474 | uri_ksk_continuation (void *cls, | ||
475 | const struct GNUNET_FS_Uri *ksk_uri, | ||
476 | const char *emsg) | ||
477 | { | ||
478 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv; | ||
479 | const struct GNUNET_CRYPTO_PrivateKey *pk; | ||
480 | |||
481 | if (NULL != emsg) | ||
482 | { | ||
483 | fprintf (stderr, "%s\n", emsg); | ||
484 | ret = 1; | ||
485 | } | ||
486 | if (NULL == namespace) | ||
487 | { | ||
488 | GNUNET_SCHEDULER_shutdown (); | ||
489 | return; | ||
490 | } | ||
491 | pk = GNUNET_IDENTITY_ego_get_private_key (namespace); | ||
492 | if (GNUNET_PUBLIC_KEY_TYPE_ECDSA != ntohl (pk->type)) | ||
493 | return; | ||
494 | priv = &pk->ecdsa_key; | ||
495 | GNUNET_FS_publish_sks (ctx, | ||
496 | priv, | ||
497 | this_id, | ||
498 | next_id, | ||
499 | meta, | ||
500 | uri, | ||
501 | &bo, | ||
502 | GNUNET_FS_PUBLISH_OPTION_NONE, | ||
503 | &uri_sks_continuation, | ||
504 | NULL); | ||
505 | } | ||
506 | |||
507 | |||
508 | /** | ||
509 | * Iterate over the results from the directory scan and extract | ||
510 | * the desired information for the publishing operation. | ||
511 | * | ||
512 | * @param item root with the data from the directory scan | ||
513 | * @return handle with the information for the publishing operation | ||
514 | */ | ||
515 | static struct GNUNET_FS_FileInformation * | ||
516 | get_file_information (struct GNUNET_FS_ShareTreeItem *item) | ||
517 | { | ||
518 | struct GNUNET_FS_FileInformation *fi; | ||
519 | struct GNUNET_FS_FileInformation *fic; | ||
520 | struct GNUNET_FS_ShareTreeItem *child; | ||
521 | |||
522 | if (GNUNET_YES == item->is_directory) | ||
523 | { | ||
524 | if (NULL == item->meta) | ||
525 | item->meta = GNUNET_FS_meta_data_create (); | ||
526 | GNUNET_FS_meta_data_delete (item->meta, | ||
527 | EXTRACTOR_METATYPE_MIMETYPE, | ||
528 | NULL, | ||
529 | 0); | ||
530 | GNUNET_FS_meta_data_make_directory (item->meta); | ||
531 | if (NULL == item->ksk_uri) | ||
532 | { | ||
533 | const char *mime = GNUNET_FS_DIRECTORY_MIME; | ||
534 | item->ksk_uri = GNUNET_FS_uri_ksk_create_from_args (1, &mime); | ||
535 | } | ||
536 | else | ||
537 | GNUNET_FS_uri_ksk_add_keyword (item->ksk_uri, | ||
538 | GNUNET_FS_DIRECTORY_MIME, | ||
539 | GNUNET_NO); | ||
540 | fi = GNUNET_FS_file_information_create_empty_directory (ctx, | ||
541 | NULL, | ||
542 | item->ksk_uri, | ||
543 | item->meta, | ||
544 | &bo, | ||
545 | item->filename); | ||
546 | for (child = item->children_head; child; child = child->next) | ||
547 | { | ||
548 | fic = get_file_information (child); | ||
549 | GNUNET_break (GNUNET_OK == GNUNET_FS_file_information_add (fi, fic)); | ||
550 | } | ||
551 | } | ||
552 | else | ||
553 | { | ||
554 | fi = GNUNET_FS_file_information_create_from_file (ctx, | ||
555 | NULL, | ||
556 | item->filename, | ||
557 | item->ksk_uri, | ||
558 | item->meta, | ||
559 | ! do_insert, | ||
560 | &bo); | ||
561 | } | ||
562 | return fi; | ||
563 | } | ||
564 | |||
565 | |||
566 | /** | ||
567 | * We've finished scanning the directory and optimized the meta data. | ||
568 | * Begin the publication process. | ||
569 | * | ||
570 | * @param directory_scan_result result from the directory scan, freed in this function | ||
571 | */ | ||
572 | static void | ||
573 | directory_trim_complete (struct GNUNET_FS_ShareTreeItem *directory_scan_result) | ||
574 | { | ||
575 | struct GNUNET_FS_FileInformation *fi; | ||
576 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv; | ||
577 | const struct GNUNET_CRYPTO_PrivateKey *pk; | ||
578 | |||
579 | fi = get_file_information (directory_scan_result); | ||
580 | GNUNET_FS_share_tree_free (directory_scan_result); | ||
581 | if (NULL == fi) | ||
582 | { | ||
583 | fprintf (stderr, "%s", _ ("Could not publish\n")); | ||
584 | ret = 1; | ||
585 | GNUNET_SCHEDULER_shutdown (); | ||
586 | return; | ||
587 | } | ||
588 | GNUNET_FS_file_information_inspect (fi, &publish_inspector, NULL); | ||
589 | if (extract_only) | ||
590 | { | ||
591 | GNUNET_FS_file_information_destroy (fi, NULL, NULL); | ||
592 | GNUNET_SCHEDULER_shutdown (); | ||
593 | return; | ||
594 | } | ||
595 | priv = NULL; | ||
596 | if (NULL != namespace) | ||
597 | { | ||
598 | pk = GNUNET_IDENTITY_ego_get_private_key (namespace); | ||
599 | GNUNET_assert (GNUNET_PUBLIC_KEY_TYPE_ECDSA == ntohl (pk->type)); | ||
600 | priv = &pk->ecdsa_key; | ||
601 | } | ||
602 | pc = GNUNET_FS_publish_start (ctx, | ||
603 | fi, | ||
604 | priv, | ||
605 | this_id, | ||
606 | next_id, | ||
607 | (do_simulate) | ||
608 | ? GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY | ||
609 | : GNUNET_FS_PUBLISH_OPTION_NONE); | ||
610 | if (NULL == pc) | ||
611 | { | ||
612 | fprintf (stderr, "%s", _ ("Could not start publishing.\n")); | ||
613 | ret = 1; | ||
614 | GNUNET_SCHEDULER_shutdown (); | ||
615 | return; | ||
616 | } | ||
617 | } | ||
618 | |||
619 | |||
620 | /** | ||
621 | * Function called by the directory scanner as we build the tree | ||
622 | * that we will need to publish later. | ||
623 | * | ||
624 | * @param cls closure | ||
625 | * @param filename which file we are making progress on | ||
626 | * @param is_directory #GNUNET_YES if this is a directory, | ||
627 | * #GNUNET_NO if this is a file | ||
628 | * #GNUNET_SYSERR if it is neither (or unknown) | ||
629 | * @param reason kind of progress we are making | ||
630 | */ | ||
631 | static void | ||
632 | directory_scan_cb (void *cls, | ||
633 | const char *filename, | ||
634 | int is_directory, | ||
635 | enum GNUNET_FS_DirScannerProgressUpdateReason reason) | ||
636 | { | ||
637 | struct GNUNET_FS_ShareTreeItem *directory_scan_result; | ||
638 | |||
639 | switch (reason) | ||
640 | { | ||
641 | case GNUNET_FS_DIRSCANNER_FILE_START: | ||
642 | if (verbose > 1) | ||
643 | { | ||
644 | if (is_directory == GNUNET_YES) | ||
645 | fprintf (stdout, _ ("Scanning directory `%s'.\n"), filename); | ||
646 | else | ||
647 | fprintf (stdout, _ ("Scanning file `%s'.\n"), filename); | ||
648 | } | ||
649 | break; | ||
650 | |||
651 | case GNUNET_FS_DIRSCANNER_FILE_IGNORED: | ||
652 | fprintf (stderr, | ||
653 | _ ("There was trouble processing file `%s', skipping it.\n"), | ||
654 | filename); | ||
655 | break; | ||
656 | |||
657 | case GNUNET_FS_DIRSCANNER_ALL_COUNTED: | ||
658 | if (verbose) | ||
659 | fprintf (stdout, "%s", _ ("Preprocessing complete.\n")); | ||
660 | break; | ||
661 | |||
662 | case GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED: | ||
663 | if (verbose > 2) | ||
664 | fprintf (stdout, | ||
665 | _ ("Extracting meta data from file `%s' complete.\n"), | ||
666 | filename); | ||
667 | break; | ||
668 | |||
669 | case GNUNET_FS_DIRSCANNER_FINISHED: | ||
670 | if (verbose > 1) | ||
671 | fprintf (stdout, "%s", _ ("Meta data extraction has finished.\n")); | ||
672 | directory_scan_result = GNUNET_FS_directory_scan_get_result (ds); | ||
673 | ds = NULL; | ||
674 | GNUNET_FS_share_tree_trim (directory_scan_result); | ||
675 | directory_trim_complete (directory_scan_result); | ||
676 | break; | ||
677 | |||
678 | case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR: | ||
679 | fprintf (stdout, "%s", _ ("Error scanning directory.\n")); | ||
680 | ret = 1; | ||
681 | GNUNET_SCHEDULER_shutdown (); | ||
682 | break; | ||
683 | |||
684 | default: | ||
685 | GNUNET_assert (0); | ||
686 | break; | ||
687 | } | ||
688 | fflush (stdout); | ||
689 | } | ||
690 | |||
691 | |||
692 | /** | ||
693 | * Continuation proceeding with initialization after identity subsystem | ||
694 | * has been initialized. | ||
695 | * | ||
696 | * @param args0 filename to publish | ||
697 | */ | ||
698 | static void | ||
699 | identity_continuation (const char *args0) | ||
700 | { | ||
701 | char *ex; | ||
702 | char *emsg; | ||
703 | |||
704 | if ((NULL != pseudonym) && (NULL == namespace)) | ||
705 | { | ||
706 | fprintf (stderr, _ ("Selected pseudonym `%s' unknown\n"), pseudonym); | ||
707 | ret = 1; | ||
708 | GNUNET_SCHEDULER_shutdown (); | ||
709 | return; | ||
710 | } | ||
711 | if (NULL != uri_string) | ||
712 | { | ||
713 | emsg = NULL; | ||
714 | if (NULL == (uri = GNUNET_FS_uri_parse (uri_string, &emsg))) | ||
715 | { | ||
716 | fprintf (stderr, _ ("Failed to parse URI: %s\n"), emsg); | ||
717 | GNUNET_free (emsg); | ||
718 | ret = 1; | ||
719 | GNUNET_SCHEDULER_shutdown (); | ||
720 | return; | ||
721 | } | ||
722 | GNUNET_FS_publish_ksk (ctx, | ||
723 | topKeywords, | ||
724 | meta, | ||
725 | uri, | ||
726 | &bo, | ||
727 | GNUNET_FS_PUBLISH_OPTION_NONE, | ||
728 | &uri_ksk_continuation, | ||
729 | NULL); | ||
730 | return; | ||
731 | } | ||
732 | if (GNUNET_OK != | ||
733 | GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS", &ex)) | ||
734 | ex = NULL; | ||
735 | if (0 != access (args0, R_OK)) | ||
736 | { | ||
737 | fprintf (stderr, | ||
738 | _ ("Failed to access `%s': %s\n"), | ||
739 | args0, | ||
740 | strerror (errno)); | ||
741 | GNUNET_free (ex); | ||
742 | return; | ||
743 | } | ||
744 | ds = GNUNET_FS_directory_scan_start (args0, | ||
745 | disable_extractor, | ||
746 | ex, | ||
747 | &directory_scan_cb, | ||
748 | NULL); | ||
749 | if (NULL == ds) | ||
750 | { | ||
751 | fprintf ( | ||
752 | stderr, | ||
753 | "%s", | ||
754 | _ ( | ||
755 | "Failed to start meta directory scanner. Is gnunet-helper-publish-fs installed?\n")); | ||
756 | GNUNET_free (ex); | ||
757 | return; | ||
758 | } | ||
759 | GNUNET_free (ex); | ||
760 | } | ||
761 | |||
762 | |||
763 | /** | ||
764 | * Function called by identity service with known pseudonyms. | ||
765 | * | ||
766 | * @param cls closure with 'const char *' of filename to publish | ||
767 | * @param ego ego handle | ||
768 | * @param ctx context for application to store data for this ego | ||
769 | * (during the lifetime of this process, initially NULL) | ||
770 | * @param name name assigned by the user for this ego, | ||
771 | * NULL if the user just deleted the ego and it | ||
772 | * must thus no longer be used | ||
773 | */ | ||
774 | static void | ||
775 | identity_cb (void *cls, | ||
776 | struct GNUNET_IDENTITY_Ego *ego, | ||
777 | void **ctx, | ||
778 | const char *name) | ||
779 | { | ||
780 | const char *args0 = cls; | ||
781 | |||
782 | if (NULL == ego) | ||
783 | { | ||
784 | identity_continuation (args0); | ||
785 | return; | ||
786 | } | ||
787 | if (NULL == name) | ||
788 | return; | ||
789 | if (0 == strcmp (name, pseudonym)) | ||
790 | namespace = ego; | ||
791 | } | ||
792 | |||
793 | |||
794 | /** | ||
795 | * Main function that will be run by the scheduler. | ||
796 | * | ||
797 | * @param cls closure | ||
798 | * @param args remaining command-line arguments | ||
799 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
800 | * @param c configuration | ||
801 | */ | ||
802 | static void | ||
803 | run (void *cls, | ||
804 | char *const *args, | ||
805 | const char *cfgfile, | ||
806 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
807 | { | ||
808 | /* check arguments */ | ||
809 | if ((NULL != uri_string) && (extract_only)) | ||
810 | { | ||
811 | printf (_ ("Cannot extract metadata from a URI!\n")); | ||
812 | ret = -1; | ||
813 | return; | ||
814 | } | ||
815 | if (((NULL == uri_string) || (extract_only)) && | ||
816 | ((NULL == args[0]) || (NULL != args[1]))) | ||
817 | { | ||
818 | printf (_ ("You must specify one and only one filename for insertion.\n")); | ||
819 | ret = -1; | ||
820 | return; | ||
821 | } | ||
822 | if ((NULL != uri_string) && (NULL != args[0])) | ||
823 | { | ||
824 | printf (_ ("You must NOT specify an URI and a filename.\n")); | ||
825 | ret = -1; | ||
826 | return; | ||
827 | } | ||
828 | if (NULL != pseudonym) | ||
829 | { | ||
830 | if (NULL == this_id) | ||
831 | { | ||
832 | fprintf (stderr, | ||
833 | _ ("Option `%s' is required when using option `%s'.\n"), | ||
834 | "-t", | ||
835 | "-P"); | ||
836 | ret = -1; | ||
837 | return; | ||
838 | } | ||
839 | } | ||
840 | else | ||
841 | { /* ordinary insertion checks */ | ||
842 | if (NULL != next_id) | ||
843 | { | ||
844 | fprintf (stderr, | ||
845 | _ ("Option `%s' makes no sense without option `%s'.\n"), | ||
846 | "-N", | ||
847 | "-P"); | ||
848 | ret = -1; | ||
849 | return; | ||
850 | } | ||
851 | if (NULL != this_id) | ||
852 | { | ||
853 | fprintf (stderr, | ||
854 | _ ("Option `%s' makes no sense without option `%s'.\n"), | ||
855 | "-t", | ||
856 | "-P"); | ||
857 | ret = -1; | ||
858 | return; | ||
859 | } | ||
860 | } | ||
861 | cfg = c; | ||
862 | ctx = GNUNET_FS_start (cfg, | ||
863 | "gnunet-publish", | ||
864 | &progress_cb, | ||
865 | NULL, | ||
866 | GNUNET_FS_FLAGS_NONE, | ||
867 | GNUNET_FS_OPTIONS_END); | ||
868 | if (NULL == ctx) | ||
869 | { | ||
870 | fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS"); | ||
871 | ret = 1; | ||
872 | return; | ||
873 | } | ||
874 | GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL); | ||
875 | if (NULL != pseudonym) | ||
876 | identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, args[0]); | ||
877 | else | ||
878 | identity_continuation (args[0]); | ||
879 | } | ||
880 | |||
881 | |||
882 | /** | ||
883 | * The main function to publish content to GNUnet. | ||
884 | * | ||
885 | * @param argc number of arguments from the command line | ||
886 | * @param argv command line arguments | ||
887 | * @return 0 ok, 1 on error | ||
888 | */ | ||
889 | int | ||
890 | main (int argc, char *const *argv) | ||
891 | { | ||
892 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
893 | { GNUNET_GETOPT_option_uint ('a', | ||
894 | "anonymity", | ||
895 | "LEVEL", | ||
896 | gettext_noop ( | ||
897 | "set the desired LEVEL of sender-anonymity"), | ||
898 | &bo.anonymity_level), | ||
899 | GNUNET_GETOPT_option_flag ( | ||
900 | 'D', | ||
901 | "disable-extractor", | ||
902 | gettext_noop ("do not use libextractor to add keywords or metadata"), | ||
903 | &disable_extractor), | ||
904 | GNUNET_GETOPT_option_flag ('E', | ||
905 | "enable-creation-time", | ||
906 | gettext_noop ( | ||
907 | "enable adding the creation time to the " | ||
908 | "metadata of the uploaded file"), | ||
909 | &enable_creation_time), | ||
910 | GNUNET_GETOPT_option_flag ('e', | ||
911 | "extract", | ||
912 | gettext_noop ( | ||
913 | "print list of extracted keywords that would " | ||
914 | "be used, but do not perform upload"), | ||
915 | &extract_only), | ||
916 | GNUNET_FS_GETOPT_KEYWORDS ( | ||
917 | 'k', | ||
918 | "key", | ||
919 | "KEYWORD", | ||
920 | gettext_noop ( | ||
921 | "add an additional keyword for the top-level " | ||
922 | "file or directory (this option can be specified multiple times)"), | ||
923 | &topKeywords), | ||
924 | GNUNET_FS_GETOPT_METADATA ( | ||
925 | 'm', | ||
926 | "meta", | ||
927 | "TYPE:VALUE", | ||
928 | gettext_noop ("set the meta-data for the given TYPE to the given VALUE"), | ||
929 | &meta), | ||
930 | GNUNET_GETOPT_option_flag ( | ||
931 | 'n', | ||
932 | "noindex", | ||
933 | gettext_noop ("do not index, perform full insertion (stores " | ||
934 | "entire file in encrypted form in GNUnet database)"), | ||
935 | &do_insert), | ||
936 | GNUNET_GETOPT_option_string ( | ||
937 | 'N', | ||
938 | "next", | ||
939 | "ID", | ||
940 | gettext_noop ("specify ID of an updated version to be " | ||
941 | "published in the future (for namespace insertions only)"), | ||
942 | &next_id), | ||
943 | GNUNET_GETOPT_option_uint ('p', | ||
944 | "priority", | ||
945 | "PRIORITY", | ||
946 | gettext_noop ( | ||
947 | "specify the priority of the content"), | ||
948 | &bo.content_priority), | ||
949 | GNUNET_GETOPT_option_string ('P', | ||
950 | "pseudonym", | ||
951 | "NAME", | ||
952 | gettext_noop ( | ||
953 | "publish the files under the pseudonym " | ||
954 | "NAME (place file into namespace)"), | ||
955 | &pseudonym), | ||
956 | GNUNET_GETOPT_option_uint ('r', | ||
957 | "replication", | ||
958 | "LEVEL", | ||
959 | gettext_noop ( | ||
960 | "set the desired replication LEVEL"), | ||
961 | &bo.replication_level), | ||
962 | GNUNET_GETOPT_option_flag ('s', | ||
963 | "simulate-only", | ||
964 | gettext_noop ( | ||
965 | "only simulate the process but do not do " | ||
966 | "any actual publishing (useful to compute URIs)"), | ||
967 | &do_simulate), | ||
968 | GNUNET_GETOPT_option_string ('t', | ||
969 | "this", | ||
970 | "ID", | ||
971 | gettext_noop ( | ||
972 | "set the ID of this version of the publication " | ||
973 | "(for namespace insertions only)"), | ||
974 | &this_id), | ||
975 | GNUNET_GETOPT_option_string ( | ||
976 | 'u', | ||
977 | "uri", | ||
978 | "URI", | ||
979 | gettext_noop ( | ||
980 | "URI to be published (can be used instead of passing a " | ||
981 | "file to add keywords to the file with the respective URI)"), | ||
982 | &uri_string), | ||
983 | |||
984 | GNUNET_GETOPT_option_verbose (&verbose), | ||
985 | |||
986 | GNUNET_GETOPT_OPTION_END }; | ||
987 | |||
988 | bo.expiration_time = | ||
989 | GNUNET_TIME_year_to_time (GNUNET_TIME_get_current_year () + 2); | ||
990 | |||
991 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
992 | return 2; | ||
993 | ret = | ||
994 | (GNUNET_OK == | ||
995 | GNUNET_PROGRAM_run (argc, | ||
996 | argv, | ||
997 | "gnunet-publish [OPTIONS] FILENAME", | ||
998 | gettext_noop ("Publish a file or directory on GNUnet"), | ||
999 | options, | ||
1000 | &run, | ||
1001 | NULL)) | ||
1002 | ? ret | ||
1003 | : 1; | ||
1004 | GNUNET_free_nz ((void *) argv); | ||
1005 | return ret; | ||
1006 | } | ||
1007 | |||
1008 | |||
1009 | /* end of gnunet-publish.c */ | ||
diff --git a/src/fs/gnunet-search.c b/src/fs/gnunet-search.c deleted file mode 100644 index a72cf97cc..000000000 --- a/src/fs/gnunet-search.c +++ /dev/null | |||
@@ -1,801 +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 "platform.h" | ||
30 | #include <ctype.h> | ||
31 | #include <inttypes.h> | ||
32 | #include <limits.h> | ||
33 | |||
34 | #include "gnunet_fs_service.h" | ||
35 | |||
36 | |||
37 | #define GNUNET_SEARCH_log(kind, ...) \ | ||
38 | GNUNET_log_from (kind, "gnunet-search", __VA_ARGS__) | ||
39 | |||
40 | |||
41 | /* The default settings that we use for the printed output */ | ||
42 | |||
43 | #define DEFAULT_DIR_FORMAT "#%n:\ngnunet-download -o \"%f\" -R %u\n\n" | ||
44 | #define HELP_DEFAULT_DIR_FORMAT "#%n:\\ngnunet-download -o \"%f\" -R %u\\n\\n" | ||
45 | #define DEFAULT_FILE_FORMAT "#%n:\ngnunet-download -o \"%f\" %u\n\n" | ||
46 | #define HELP_DEFAULT_FILE_FORMAT "#%n:\\ngnunet-download -o \"%f\" %u\\n\\n" | ||
47 | #define VERB_DEFAULT_DIR_FORMAT DEFAULT_DIR_FORMAT "%a\n" | ||
48 | #define VERB_DEFAULT_FILE_FORMAT DEFAULT_FILE_FORMAT "%a\n" | ||
49 | |||
50 | #if HAVE_LIBEXTRACTOR | ||
51 | #define DEFAULT_META_FORMAT " %t: %p\n" | ||
52 | #define HELP_DEFAULT_META_FORMAT " %t: %p\\n" | ||
53 | #define HELP_EXTRACTOR_TEXTADD ", %t" | ||
54 | #else | ||
55 | #define DEFAULT_META_FORMAT " MetaType #%i: %p\n" | ||
56 | #define HELP_DEFAULT_META_FORMAT " MetaType #%i: %p\\n" | ||
57 | #define HELP_EXTRACTOR_TEXTADD "" | ||
58 | #endif | ||
59 | |||
60 | #define GENERIC_DIRECTORY_NAME "collection" | ||
61 | #define GENERIC_FILE_NAME "no-name" | ||
62 | #define GENERIC_FILE_MIMETYPE "application/octet-stream" | ||
63 | |||
64 | |||
65 | enum GNUNET_SEARCH_MetadataPrinterFlags | ||
66 | { | ||
67 | METADATA_PRINTER_FLAG_NONE = 0, | ||
68 | METADATA_PRINTER_FLAG_ONE_RUN = 1, | ||
69 | METADATA_PRINTER_FLAG_HAVE_TYPE = 2 | ||
70 | }; | ||
71 | |||
72 | |||
73 | struct GNUNET_SEARCH_MetadataPrinterInfo | ||
74 | { | ||
75 | unsigned int counter; | ||
76 | unsigned int flags; | ||
77 | int type; | ||
78 | }; | ||
79 | |||
80 | |||
81 | static int ret; | ||
82 | |||
83 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
84 | |||
85 | static struct GNUNET_FS_Handle *ctx; | ||
86 | |||
87 | static struct GNUNET_FS_SearchContext *sc; | ||
88 | |||
89 | static char *output_filename; | ||
90 | |||
91 | static char *format_string; | ||
92 | |||
93 | static char *dir_format_string; | ||
94 | |||
95 | static char *meta_format_string; | ||
96 | |||
97 | static struct GNUNET_FS_DirectoryBuilder *db; | ||
98 | |||
99 | static unsigned int anonymity = 1; | ||
100 | |||
101 | /** | ||
102 | * Timeout for the search, 0 means to wait for CTRL-C. | ||
103 | */ | ||
104 | static struct GNUNET_TIME_Relative timeout; | ||
105 | |||
106 | static unsigned int results_limit; | ||
107 | |||
108 | static unsigned int results; | ||
109 | |||
110 | static unsigned int verbose; | ||
111 | |||
112 | static int bookmark_only; | ||
113 | |||
114 | static int local_only; | ||
115 | |||
116 | static int silent_mode; | ||
117 | |||
118 | static struct GNUNET_SCHEDULER_Task *tt; | ||
119 | |||
120 | static int stop_searching; | ||
121 | |||
122 | |||
123 | /** | ||
124 | * Print the escape sequence at the beginning of a string. | ||
125 | * | ||
126 | * @param esc a string that **must** begin with a backslash (the function only | ||
127 | * assumes that it does, but does not check) | ||
128 | * @return the fragment that follows what has been printed | ||
129 | * @author madmurphy | ||
130 | * | ||
131 | * If `"\\nfoo"` is passed as argument, this function prints a new line and | ||
132 | * returns `"foo"` | ||
133 | */ | ||
134 | static const char * | ||
135 | print_escape_sequence (const char *const esc) | ||
136 | { | ||
137 | unsigned int probe; | ||
138 | const char *cursor = esc + 1; | ||
139 | char tmp; | ||
140 | switch (*cursor) | ||
141 | { | ||
142 | /* Trivia */ | ||
143 | case '\\': putchar ('\\'); return cursor + 1; | ||
144 | case 'a': putchar ('\a'); return cursor + 1; | ||
145 | case 'b': putchar ('\b'); return cursor + 1; | ||
146 | case 'e': putchar ('\x1B'); return cursor + 1; | ||
147 | case 'f': putchar ('\f'); return cursor + 1; | ||
148 | case 'n': putchar ('\n'); return cursor + 1; | ||
149 | case 'r': putchar ('\r'); return cursor + 1; | ||
150 | case 't': putchar ('\t'); return cursor + 1; | ||
151 | case 'v': putchar ('\v'); return cursor + 1; | ||
152 | |||
153 | /* Possibly hexadecimal code point */ | ||
154 | case 'x': | ||
155 | probe = 0; | ||
156 | while (probe < 256 && isxdigit ((tmp = *++cursor))) | ||
157 | probe = (probe << 4) + tmp - (tmp > 96 ? 87 : tmp > 64 ? 55 : 48); | ||
158 | goto maybe_codepoint; | ||
159 | |||
160 | /* Possibly octal code point */ | ||
161 | case '0': case '1': case '2': case '3': | ||
162 | case '4': case '5': case '6': case '7': | ||
163 | probe = *cursor++ - 48; | ||
164 | do probe = (probe << 3) + *cursor++ - 48; | ||
165 | while (probe < 256 && cursor < esc + 4 && *cursor > 47 && *cursor < 56); | ||
166 | goto maybe_codepoint; | ||
167 | |||
168 | /* Boredom */ | ||
169 | case '\0': putchar ('\\'); return cursor; | ||
170 | default: printf ("\\%c", *cursor); return cursor + 1; | ||
171 | } | ||
172 | |||
173 | maybe_codepoint: | ||
174 | if (probe < 256) | ||
175 | putchar (probe); | ||
176 | else | ||
177 | fwrite (esc, 1, cursor - esc, stdout); | ||
178 | return cursor; | ||
179 | } | ||
180 | |||
181 | |||
182 | /** | ||
183 | * Type of a function that libextractor calls for each | ||
184 | * meta data item found. | ||
185 | * | ||
186 | * @param cls closure (user-defined, used for the iteration info) | ||
187 | * @param plugin_name name of the plugin that produced this value; | ||
188 | * special values can be used (e.g. '<zlib>' for zlib being | ||
189 | * used in the main libextractor library and yielding | ||
190 | * meta data). | ||
191 | * @param type libextractor-type describing the meta data | ||
192 | * @param format basic format information about data | ||
193 | * @param data_mime_type mime-type of data (not of the original file); | ||
194 | * can be NULL (if mime-type is not known) | ||
195 | * @param data actual meta-data found | ||
196 | * @param data_size number of bytes in @a data | ||
197 | * @return 0 to continue extracting, 1 to abort | ||
198 | */ | ||
199 | static int | ||
200 | item_printer (void *const cls, | ||
201 | const char *const plugin_name, | ||
202 | const enum EXTRACTOR_MetaType type, | ||
203 | const enum EXTRACTOR_MetaFormat format, | ||
204 | const char *const data_mime_type, | ||
205 | const char *const data, | ||
206 | const size_t data_size) | ||
207 | { | ||
208 | #define info ((struct GNUNET_SEARCH_MetadataPrinterInfo *) cls) | ||
209 | if ((format != EXTRACTOR_METAFORMAT_UTF8 && | ||
210 | format != EXTRACTOR_METAFORMAT_C_STRING) || | ||
211 | type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME) | ||
212 | return 0; | ||
213 | info->counter++; | ||
214 | if ((info->flags & METADATA_PRINTER_FLAG_HAVE_TYPE) && type != info->type) | ||
215 | return 0; | ||
216 | |||
217 | const char *cursor = meta_format_string; | ||
218 | const char *next_spec = strchr (cursor, '%'); | ||
219 | const char *next_esc = strchr (cursor, '\\'); | ||
220 | |||
221 | parse_format: | ||
222 | |||
223 | /* If an escape sequence exists before the next format specifier... */ | ||
224 | if (next_esc && (! next_spec || next_esc < next_spec)) | ||
225 | { | ||
226 | if (next_esc > cursor) | ||
227 | fwrite (cursor, 1, next_esc - cursor, stdout); | ||
228 | |||
229 | cursor = print_escape_sequence (next_esc); | ||
230 | next_esc = strchr (cursor, '\\'); | ||
231 | goto parse_format; | ||
232 | } | ||
233 | |||
234 | /* If a format specifier exists before the next escape sequence... */ | ||
235 | if (next_spec && (! next_esc || next_spec < next_esc)) | ||
236 | { | ||
237 | if (next_spec > cursor) | ||
238 | fwrite (cursor, 1, next_spec - cursor, stdout); | ||
239 | |||
240 | switch (*++next_spec) | ||
241 | { | ||
242 | case '%': putchar ('%'); break; | ||
243 | case 'i': printf ("%d", type); break; | ||
244 | case 'l': printf ("%lu", (long unsigned int) data_size); break; | ||
245 | case 'n': printf ("%u", info->counter); break; | ||
246 | case 'p': printf ("%s", data); break; | ||
247 | #if HAVE_LIBEXTRACTOR | ||
248 | case 't': | ||
249 | printf ("%s", | ||
250 | dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN, | ||
251 | EXTRACTOR_metatype_to_string (type))); | ||
252 | break; | ||
253 | #endif | ||
254 | case 'w': printf ("%s", plugin_name); break; | ||
255 | case '\0': putchar ('%'); return 0; | ||
256 | default: printf ("%%%c", *next_spec); break; | ||
257 | } | ||
258 | cursor = next_spec + 1; | ||
259 | next_spec = strchr (cursor, '%'); | ||
260 | goto parse_format; | ||
261 | } | ||
262 | |||
263 | if (*cursor) | ||
264 | printf ("%s", cursor); | ||
265 | |||
266 | return info->flags & METADATA_PRINTER_FLAG_ONE_RUN; | ||
267 | #undef info | ||
268 | } | ||
269 | |||
270 | |||
271 | /** | ||
272 | * Print a search result according to the current formats | ||
273 | * | ||
274 | * @param filename the filename for this result | ||
275 | * @param uri the `struct GNUNET_FS_Uri` this result refers to | ||
276 | * @param metadata the `struct GNUNET_FS_MetaData` associated with this | ||
277 | result | ||
278 | * @param resultnum the result number | ||
279 | * @param is_directory GNUNET_YES if this is a directory, otherwise GNUNET_NO | ||
280 | * @author madmurphy | ||
281 | */ | ||
282 | static void | ||
283 | print_search_result (const char *const filename, | ||
284 | const struct GNUNET_FS_Uri *const uri, | ||
285 | const struct GNUNET_FS_MetaData *const metadata, | ||
286 | const unsigned int resultnum, | ||
287 | const int is_directory) | ||
288 | { | ||
289 | |||
290 | const char *cursor = GNUNET_YES == is_directory ? | ||
291 | dir_format_string | ||
292 | : format_string; | ||
293 | |||
294 | const char *next_spec = strchr (cursor, '%'); | ||
295 | const char *next_esc = strchr (cursor, '\\'); | ||
296 | char *placeholder; | ||
297 | struct GNUNET_SEARCH_MetadataPrinterInfo info; | ||
298 | |||
299 | parse_format: | ||
300 | /* If an escape sequence exists before the next format specifier... */ | ||
301 | if (next_esc && (! next_spec || next_esc < next_spec)) | ||
302 | { | ||
303 | if (next_esc > cursor) | ||
304 | fwrite (cursor, 1, next_esc - cursor, stdout); | ||
305 | |||
306 | cursor = print_escape_sequence (next_esc); | ||
307 | next_esc = strchr (cursor, '\\'); | ||
308 | goto parse_format; | ||
309 | } | ||
310 | |||
311 | /* If a format specifier exists before the next escape sequence... */ | ||
312 | if (next_spec && (! next_esc || next_spec < next_esc)) | ||
313 | { | ||
314 | if (next_spec > cursor) | ||
315 | fwrite (cursor, 1, next_spec - cursor, stdout); | ||
316 | |||
317 | switch (*++next_spec) | ||
318 | { | ||
319 | /* All metadata fields */ | ||
320 | case 'a': | ||
321 | info.flags = METADATA_PRINTER_FLAG_NONE; | ||
322 | |||
323 | iterate_meta: | ||
324 | info.counter = 0; | ||
325 | GNUNET_FS_meta_data_iterate (metadata, &item_printer, &info); | ||
326 | break; | ||
327 | /* File's name */ | ||
328 | case 'f': | ||
329 | if (GNUNET_YES == is_directory) | ||
330 | { | ||
331 | printf ("%s%s", filename, GNUNET_FS_DIRECTORY_EXT); | ||
332 | break; | ||
333 | } | ||
334 | printf ("%s", filename); | ||
335 | break; | ||
336 | /* Only the first metadata field */ | ||
337 | case 'j': | ||
338 | info.flags = METADATA_PRINTER_FLAG_ONE_RUN; | ||
339 | goto iterate_meta; | ||
340 | /* File name's length */ | ||
341 | case 'l': | ||
342 | printf ("%lu", | ||
343 | (long unsigned int) (GNUNET_YES == is_directory ? | ||
344 | strlen (filename) | ||
345 | + (sizeof(GNUNET_FS_DIRECTORY_EXT) - 1) | ||
346 | : | ||
347 | strlen (filename))); | ||
348 | break; | ||
349 | /* File's mime type */ | ||
350 | case 'm': | ||
351 | if (GNUNET_YES == is_directory) | ||
352 | { | ||
353 | printf ("%s", GNUNET_FS_DIRECTORY_MIME); | ||
354 | break; | ||
355 | } | ||
356 | placeholder = GNUNET_FS_meta_data_get_by_type ( | ||
357 | metadata, | ||
358 | EXTRACTOR_METATYPE_MIMETYPE); | ||
359 | printf ("%s", placeholder ? placeholder : GENERIC_FILE_MIMETYPE); | ||
360 | GNUNET_free (placeholder); | ||
361 | break; | ||
362 | /* Result number */ | ||
363 | case 'n': printf ("%u", resultnum); break; | ||
364 | /* File's size */ | ||
365 | case 's': | ||
366 | printf ("%" PRIu64, GNUNET_FS_uri_chk_get_file_size (uri)); | ||
367 | break; | ||
368 | /* File's URI */ | ||
369 | case 'u': | ||
370 | placeholder = GNUNET_FS_uri_to_string (uri); | ||
371 | printf ("%s", placeholder); | ||
372 | GNUNET_free (placeholder); | ||
373 | break; | ||
374 | |||
375 | /* We can add as many cases as we want here... */ | ||
376 | |||
377 | /* Handle `%123#a` and `%123#j` (e.g. `%5#j` is a book title) */ | ||
378 | case '0': case '1': case '2': case '3': case '4': | ||
379 | case '5': case '6': case '7': case '8': case '9': | ||
380 | cursor = next_spec; | ||
381 | info.type = *cursor - 48; | ||
382 | while (isdigit (*++cursor) && info.type < (INT_MAX - *cursor + 48) / 10) | ||
383 | info.type = info.type * 10 + *cursor - 48; | ||
384 | if (info.type == 0 || *cursor != '#') | ||
385 | goto not_a_specifier; | ||
386 | switch (*++cursor) | ||
387 | { | ||
388 | /* All metadata fields of type `info.type` */ | ||
389 | case 'a': | ||
390 | next_spec = cursor; | ||
391 | info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE; | ||
392 | goto iterate_meta; | ||
393 | |||
394 | /* Only the first metadata field of type `info.type` */ | ||
395 | case 'j': | ||
396 | next_spec = cursor; | ||
397 | info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE | ||
398 | | METADATA_PRINTER_FLAG_ONE_RUN; | ||
399 | goto iterate_meta; | ||
400 | } | ||
401 | goto not_a_specifier; | ||
402 | |||
403 | /* All other cases */ | ||
404 | case '%': putchar ('%'); break; | ||
405 | case '\0': putchar ('%'); return; | ||
406 | |||
407 | not_a_specifier: | ||
408 | default: printf ("%%%c", *next_spec); break; | ||
409 | } | ||
410 | cursor = next_spec + 1; | ||
411 | next_spec = strchr (cursor, '%'); | ||
412 | goto parse_format; | ||
413 | } | ||
414 | |||
415 | if (*cursor) | ||
416 | printf ("%s", cursor); | ||
417 | } | ||
418 | |||
419 | |||
420 | static void | ||
421 | clean_task (void *const cls) | ||
422 | { | ||
423 | size_t dsize; | ||
424 | void *ddata; | ||
425 | |||
426 | GNUNET_FS_stop (ctx); | ||
427 | ctx = NULL; | ||
428 | if (output_filename == NULL) | ||
429 | return; | ||
430 | if (GNUNET_OK != | ||
431 | GNUNET_FS_directory_builder_finish (db, &dsize, &ddata)) | ||
432 | { | ||
433 | GNUNET_break (0); | ||
434 | GNUNET_free (output_filename); | ||
435 | return; | ||
436 | } | ||
437 | (void) GNUNET_DISK_directory_remove (output_filename); | ||
438 | if (GNUNET_OK != | ||
439 | GNUNET_DISK_fn_write (output_filename, | ||
440 | ddata, | ||
441 | dsize, | ||
442 | GNUNET_DISK_PERM_USER_READ | ||
443 | | GNUNET_DISK_PERM_USER_WRITE)) | ||
444 | { | ||
445 | GNUNET_SEARCH_log (GNUNET_ERROR_TYPE_ERROR, | ||
446 | _ ("Failed to write directory with search results to " | ||
447 | "`%s'\n"), | ||
448 | output_filename); | ||
449 | } | ||
450 | GNUNET_free (ddata); | ||
451 | GNUNET_free (output_filename); | ||
452 | } | ||
453 | |||
454 | |||
455 | /** | ||
456 | * Called by FS client to give information about the progress of an | ||
457 | * operation. | ||
458 | * | ||
459 | * @param cls closure | ||
460 | * @param info details about the event, specifying the event type | ||
461 | * and various bits about the event | ||
462 | * @return client-context (for the next progress call | ||
463 | * for this operation; should be set to NULL for | ||
464 | * SUSPEND and STOPPED events). The value returned | ||
465 | * will be passed to future callbacks in the respective | ||
466 | * field in the GNUNET_FS_ProgressInfo struct. | ||
467 | */ | ||
468 | static void * | ||
469 | progress_cb (void *const cls, | ||
470 | const struct GNUNET_FS_ProgressInfo *const info) | ||
471 | { | ||
472 | static unsigned int cnt; | ||
473 | int is_directory; | ||
474 | char *filename; | ||
475 | |||
476 | switch (info->status) | ||
477 | { | ||
478 | case GNUNET_FS_STATUS_SEARCH_START: | ||
479 | break; | ||
480 | |||
481 | case GNUNET_FS_STATUS_SEARCH_RESULT: | ||
482 | if (stop_searching) | ||
483 | break; | ||
484 | |||
485 | if (db != NULL) | ||
486 | GNUNET_FS_directory_builder_add ( | ||
487 | db, | ||
488 | info->value.search.specifics.result.uri, | ||
489 | info->value.search.specifics.result.meta, | ||
490 | NULL); | ||
491 | |||
492 | if (silent_mode) | ||
493 | break; | ||
494 | |||
495 | cnt++; | ||
496 | filename = GNUNET_FS_meta_data_get_by_type ( | ||
497 | info->value.search.specifics.result.meta, | ||
498 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); | ||
499 | is_directory = GNUNET_FS_meta_data_test_for_directory ( | ||
500 | info->value.search.specifics.result.meta); | ||
501 | if (NULL != filename) | ||
502 | { | ||
503 | while ((filename[0] != '\0') && ('/' == filename[strlen (filename) - 1])) | ||
504 | filename[strlen (filename) - 1] = '\0'; | ||
505 | GNUNET_DISK_filename_canonicalize (filename); | ||
506 | } | ||
507 | print_search_result (filename ? | ||
508 | filename | ||
509 | : is_directory ? | ||
510 | GENERIC_DIRECTORY_NAME | ||
511 | : | ||
512 | GENERIC_FILE_NAME, | ||
513 | info->value.search.specifics.result.uri, | ||
514 | info->value.search.specifics.result.meta, | ||
515 | cnt, | ||
516 | is_directory); | ||
517 | fflush (stdout); | ||
518 | GNUNET_free (filename); | ||
519 | results++; | ||
520 | if ((results_limit > 0) && (results >= results_limit)) | ||
521 | { | ||
522 | GNUNET_SCHEDULER_shutdown (); | ||
523 | /* otherwise the function might keep printing results for a while... */ | ||
524 | stop_searching = GNUNET_YES; | ||
525 | } | ||
526 | break; | ||
527 | |||
528 | case GNUNET_FS_STATUS_SEARCH_UPDATE: | ||
529 | case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED: | ||
530 | /* ignore */ | ||
531 | break; | ||
532 | |||
533 | case GNUNET_FS_STATUS_SEARCH_ERROR: | ||
534 | GNUNET_SEARCH_log (GNUNET_ERROR_TYPE_ERROR, | ||
535 | _ ("Error searching: %s.\n"), | ||
536 | info->value.search.specifics.error.message); | ||
537 | GNUNET_SCHEDULER_shutdown (); | ||
538 | break; | ||
539 | |||
540 | case GNUNET_FS_STATUS_SEARCH_STOPPED: | ||
541 | GNUNET_SCHEDULER_add_now (&clean_task, NULL); | ||
542 | break; | ||
543 | |||
544 | default: | ||
545 | GNUNET_SEARCH_log (GNUNET_ERROR_TYPE_ERROR, | ||
546 | _ ("Unexpected status: %d\n"), | ||
547 | info->status); | ||
548 | break; | ||
549 | } | ||
550 | return NULL; | ||
551 | } | ||
552 | |||
553 | |||
554 | static void | ||
555 | shutdown_task (void *const cls) | ||
556 | { | ||
557 | if (sc != NULL) | ||
558 | { | ||
559 | GNUNET_FS_search_stop (sc); | ||
560 | sc = NULL; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | |||
565 | static void | ||
566 | timeout_task (void *const cls) | ||
567 | { | ||
568 | tt = NULL; | ||
569 | stop_searching = GNUNET_YES; | ||
570 | GNUNET_SCHEDULER_shutdown (); | ||
571 | } | ||
572 | |||
573 | |||
574 | /** | ||
575 | * Main function that will be run by the scheduler. | ||
576 | * | ||
577 | * @param cls closure | ||
578 | * @param args remaining command-line arguments | ||
579 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
580 | * @param cfgarg configuration | ||
581 | */ | ||
582 | static void | ||
583 | run (void *const cls, | ||
584 | char *const *const args, | ||
585 | const char *const cfgfile, | ||
586 | const struct GNUNET_CONFIGURATION_Handle *const cfgarg) | ||
587 | { | ||
588 | struct GNUNET_FS_Uri *uri; | ||
589 | unsigned int argc; | ||
590 | enum GNUNET_FS_SearchOptions options; | ||
591 | |||
592 | if (silent_mode && bookmark_only) | ||
593 | { | ||
594 | fprintf (stderr, | ||
595 | _ ("Conflicting options --bookmark-only and --silent.\n")); | ||
596 | ret = 1; | ||
597 | return; | ||
598 | } | ||
599 | if (bookmark_only && output_filename) | ||
600 | { | ||
601 | fprintf (stderr, | ||
602 | _ ("Conflicting options --bookmark-only and --output.\n")); | ||
603 | ret = 1; | ||
604 | return; | ||
605 | } | ||
606 | if (silent_mode && ! output_filename) | ||
607 | { | ||
608 | fprintf (stderr, _ ("An output file is mandatory for silent mode.\n")); | ||
609 | ret = 1; | ||
610 | return; | ||
611 | } | ||
612 | if (NULL == dir_format_string) | ||
613 | dir_format_string = format_string ? format_string | ||
614 | : verbose ? VERB_DEFAULT_DIR_FORMAT | ||
615 | : DEFAULT_DIR_FORMAT; | ||
616 | if (NULL == format_string) | ||
617 | format_string = verbose ? VERB_DEFAULT_FILE_FORMAT | ||
618 | : DEFAULT_FILE_FORMAT; | ||
619 | if (NULL == meta_format_string) | ||
620 | meta_format_string = DEFAULT_META_FORMAT; | ||
621 | argc = 0; | ||
622 | while (NULL != args[argc]) | ||
623 | argc++; | ||
624 | uri = GNUNET_FS_uri_ksk_create_from_args (argc, (const char **) args); | ||
625 | if (NULL == uri) | ||
626 | { | ||
627 | fprintf (stderr, | ||
628 | "%s", | ||
629 | _ ("Could not create keyword URI from arguments.\n")); | ||
630 | ret = 1; | ||
631 | return; | ||
632 | } | ||
633 | if (! GNUNET_FS_uri_test_ksk (uri) && ! GNUNET_FS_uri_test_sks (uri)) | ||
634 | { | ||
635 | fprintf (stderr, | ||
636 | "%s", | ||
637 | _ ("Invalid URI. Valid URIs for searching are keyword query " | ||
638 | "URIs\n(\"gnunet://fs/ksk/...\") and namespace content URIs " | ||
639 | "(\"gnunet://fs/sks/...\").\n")); | ||
640 | GNUNET_FS_uri_destroy (uri); | ||
641 | ret = 1; | ||
642 | return; | ||
643 | } | ||
644 | if (bookmark_only) | ||
645 | { | ||
646 | char *bmstr = GNUNET_FS_uri_to_string (uri); | ||
647 | printf ("%s\n", bmstr); | ||
648 | GNUNET_free (bmstr); | ||
649 | GNUNET_FS_uri_destroy (uri); | ||
650 | ret = 0; | ||
651 | return; | ||
652 | } | ||
653 | cfg = cfgarg; | ||
654 | ctx = GNUNET_FS_start (cfg, | ||
655 | "gnunet-search", | ||
656 | &progress_cb, | ||
657 | NULL, | ||
658 | GNUNET_FS_FLAGS_NONE, | ||
659 | GNUNET_FS_OPTIONS_END); | ||
660 | if (NULL == ctx) | ||
661 | { | ||
662 | fprintf (stderr, _ ("Could not initialize the `%s` subsystem.\n"), "FS"); | ||
663 | GNUNET_FS_uri_destroy (uri); | ||
664 | ret = 1; | ||
665 | return; | ||
666 | } | ||
667 | if (output_filename != NULL) | ||
668 | db = GNUNET_FS_directory_builder_create (NULL); | ||
669 | options = GNUNET_FS_SEARCH_OPTION_NONE; | ||
670 | if (local_only) | ||
671 | options |= GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY; | ||
672 | sc = GNUNET_FS_search_start (ctx, uri, anonymity, options, NULL); | ||
673 | GNUNET_FS_uri_destroy (uri); | ||
674 | if (NULL == sc) | ||
675 | { | ||
676 | fprintf (stderr, "%s", _ ("Could not start searching.\n")); | ||
677 | GNUNET_FS_stop (ctx); | ||
678 | ret = 1; | ||
679 | return; | ||
680 | } | ||
681 | if (0 != timeout.rel_value_us) | ||
682 | tt = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_task, NULL); | ||
683 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
684 | } | ||
685 | |||
686 | |||
687 | /** | ||
688 | * The main function to search GNUnet. | ||
689 | * | ||
690 | * @param argc number of arguments from the command line | ||
691 | * @param argv command line arguments | ||
692 | * @return 0 ok, an error number on error | ||
693 | */ | ||
694 | int | ||
695 | main (int argc, char *const *argv) | ||
696 | { | ||
697 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
698 | { GNUNET_GETOPT_option_uint ( | ||
699 | 'a', | ||
700 | "anonymity", | ||
701 | "LEVEL", | ||
702 | gettext_noop ("set the desired LEVEL of receiver-anonymity (default: " | ||
703 | "1)"), | ||
704 | &anonymity), | ||
705 | GNUNET_GETOPT_option_flag ( | ||
706 | 'b', | ||
707 | "bookmark-only", | ||
708 | gettext_noop ("do not search, print only the URI that points to this " | ||
709 | "search"), | ||
710 | &bookmark_only), | ||
711 | GNUNET_GETOPT_option_string ( | ||
712 | 'F', | ||
713 | "dir-printf", | ||
714 | "FORMAT", | ||
715 | gettext_noop ("write search results for directories according to " | ||
716 | "FORMAT; accepted placeholders are: %a, %f, %j, %l, %m, " | ||
717 | "%n, %s; defaults to the value of --printf when omitted " | ||
718 | "or to `" HELP_DEFAULT_DIR_FORMAT "` if --printf is " | ||
719 | "omitted too"), | ||
720 | &dir_format_string), | ||
721 | GNUNET_GETOPT_option_string ( | ||
722 | 'f', | ||
723 | "printf", | ||
724 | "FORMAT", | ||
725 | gettext_noop ("write search results according to FORMAT; accepted " | ||
726 | "placeholders are: %a, %f, %j, %l, %m, %n, %s; defaults " | ||
727 | "to `" HELP_DEFAULT_FILE_FORMAT "` when omitted"), | ||
728 | &format_string), | ||
729 | GNUNET_GETOPT_option_string ( | ||
730 | 'i', | ||
731 | "iter-printf", | ||
732 | "FORMAT", | ||
733 | gettext_noop ("when the %a or %j placeholders appear in --printf or " | ||
734 | "--dir-printf, list each metadata property according to " | ||
735 | "FORMAT; accepted placeholders are: %i, %l, %n, %p" | ||
736 | HELP_EXTRACTOR_TEXTADD ", %w; defaults to `" | ||
737 | HELP_DEFAULT_META_FORMAT "` when omitted"), | ||
738 | &meta_format_string), | ||
739 | GNUNET_GETOPT_option_uint ('N', | ||
740 | "results", | ||
741 | "VALUE", | ||
742 | gettext_noop ("automatically terminate search " | ||
743 | "after VALUE results are found"), | ||
744 | &results_limit), | ||
745 | GNUNET_GETOPT_option_flag ( | ||
746 | 'n', | ||
747 | "no-network", | ||
748 | gettext_noop ("only search the local peer (no P2P network search)"), | ||
749 | &local_only), | ||
750 | GNUNET_GETOPT_option_string ( | ||
751 | 'o', | ||
752 | "output", | ||
753 | "FILENAME", | ||
754 | gettext_noop ("create a GNUnet directory with search results at " | ||
755 | "FILENAME (e.g. `gnunet-search --output=commons" | ||
756 | GNUNET_FS_DIRECTORY_EXT " commons`)"), | ||
757 | &output_filename), | ||
758 | GNUNET_GETOPT_option_flag ( | ||
759 | 's', | ||
760 | "silent", | ||
761 | gettext_noop ("silent mode (requires the --output argument)"), | ||
762 | &silent_mode), | ||
763 | GNUNET_GETOPT_option_relative_time ( | ||
764 | 't', | ||
765 | "timeout", | ||
766 | "DELAY", | ||
767 | gettext_noop ("automatically terminate search after DELAY; the value " | ||
768 | "given must be a number followed by a space and a time " | ||
769 | "unit, for example \"500 ms\"; without a unit it defaults " | ||
770 | "to microseconds - 1000000 = 1 second; if 0 or omitted " | ||
771 | "it means to wait for CTRL-C"), | ||
772 | &timeout), | ||
773 | GNUNET_GETOPT_option_increment_uint ( | ||
774 | 'V', | ||
775 | "verbose", | ||
776 | gettext_noop ("be verbose (append \"%a\\n\" to the default --printf and " | ||
777 | "--dir-printf arguments - ignored when these are provided " | ||
778 | "by the user)"), | ||
779 | &verbose), | ||
780 | GNUNET_GETOPT_OPTION_END }; | ||
781 | |||
782 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
783 | return 12; | ||
784 | |||
785 | if (GNUNET_SYSERR == | ||
786 | GNUNET_PROGRAM_run (argc, | ||
787 | argv, | ||
788 | "gnunet-search [OPTIONS] KEYWORD1 KEYWORD2 ...", | ||
789 | gettext_noop ("Search for files that have been " | ||
790 | "published on GNUnet\n"), | ||
791 | options, | ||
792 | &run, | ||
793 | NULL)) | ||
794 | ret = 1; | ||
795 | |||
796 | GNUNET_free_nz ((void *) argv); | ||
797 | return ret; | ||
798 | } | ||
799 | |||
800 | |||
801 | /* 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 1ab6ac2b8..000000000 --- a/src/fs/gnunet-service-fs.c +++ /dev/null | |||
@@ -1,1378 +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_protocols.h" | ||
34 | #include "gnunet_signatures.h" | ||
35 | #include "gnunet_statistics_service.h" | ||
36 | #include "gnunet_util_lib.h" | ||
37 | #include "gnunet-service-fs_cp.h" | ||
38 | #include "gnunet-service-fs_indexing.h" | ||
39 | #include "gnunet-service-fs_pe.h" | ||
40 | #include "gnunet-service-fs_pr.h" | ||
41 | #include "gnunet-service-fs_push.h" | ||
42 | #include "gnunet-service-fs_put.h" | ||
43 | #include "gnunet-service-fs_cadet.h" | ||
44 | #include "fs.h" | ||
45 | #include "fs_api.h" | ||
46 | |||
47 | /** | ||
48 | * Size for the hash map for DHT requests from the FS | ||
49 | * service. Should be about the number of concurrent | ||
50 | * DHT requests we plan to make. | ||
51 | */ | ||
52 | #define FS_DHT_HT_SIZE 1024 | ||
53 | |||
54 | |||
55 | /** | ||
56 | * How quickly do we age cover traffic? At the given | ||
57 | * time interval, remaining cover traffic counters are | ||
58 | * decremented by 1/16th. | ||
59 | */ | ||
60 | #define COVER_AGE_FREQUENCY GNUNET_TIME_relative_multiply ( \ | ||
61 | GNUNET_TIME_UNIT_SECONDS, 5) | ||
62 | |||
63 | /** | ||
64 | * Collect an instance number of statistics? May cause excessive IPC. | ||
65 | */ | ||
66 | #define INSANE_STATISTICS GNUNET_NO | ||
67 | |||
68 | |||
69 | /** | ||
70 | * Doubly-linked list of requests we are performing | ||
71 | * on behalf of the same client. | ||
72 | */ | ||
73 | struct ClientRequest | ||
74 | { | ||
75 | /** | ||
76 | * This is a doubly-linked list. | ||
77 | */ | ||
78 | struct ClientRequest *next; | ||
79 | |||
80 | /** | ||
81 | * This is a doubly-linked list. | ||
82 | */ | ||
83 | struct ClientRequest *prev; | ||
84 | |||
85 | /** | ||
86 | * Request this entry represents. | ||
87 | */ | ||
88 | struct GSF_PendingRequest *pr; | ||
89 | |||
90 | /** | ||
91 | * Client list this request belongs to. | ||
92 | */ | ||
93 | struct GSF_LocalClient *lc; | ||
94 | |||
95 | /** | ||
96 | * Task scheduled to destroy the request. | ||
97 | */ | ||
98 | struct GNUNET_SCHEDULER_Task *kill_task; | ||
99 | }; | ||
100 | |||
101 | |||
102 | /** | ||
103 | * Replies to be transmitted to the client. The actual | ||
104 | * response message is allocated after this struct. | ||
105 | */ | ||
106 | struct ClientResponse | ||
107 | { | ||
108 | /** | ||
109 | * This is a doubly-linked list. | ||
110 | */ | ||
111 | struct ClientResponse *next; | ||
112 | |||
113 | /** | ||
114 | * This is a doubly-linked list. | ||
115 | */ | ||
116 | struct ClientResponse *prev; | ||
117 | |||
118 | /** | ||
119 | * Client list entry this response belongs to. | ||
120 | */ | ||
121 | struct GSF_LocalClient *lc; | ||
122 | |||
123 | /** | ||
124 | * Number of bytes in the response. | ||
125 | */ | ||
126 | size_t msize; | ||
127 | }; | ||
128 | |||
129 | |||
130 | /** | ||
131 | * Information we track while handling an index | ||
132 | * start request from a client. | ||
133 | */ | ||
134 | struct IndexStartContext | ||
135 | { | ||
136 | /** | ||
137 | * This is a doubly linked list. | ||
138 | */ | ||
139 | struct IndexStartContext *next; | ||
140 | |||
141 | /** | ||
142 | * This is a doubly linked list. | ||
143 | */ | ||
144 | struct IndexStartContext *prev; | ||
145 | |||
146 | /** | ||
147 | * Name of the indexed file. | ||
148 | */ | ||
149 | char *filename; | ||
150 | |||
151 | /** | ||
152 | * Context for transmitting confirmation to client. | ||
153 | */ | ||
154 | struct GSF_LocalClient *lc; | ||
155 | |||
156 | /** | ||
157 | * Context for hashing of the file. | ||
158 | */ | ||
159 | struct GNUNET_CRYPTO_FileHashContext *fhc; | ||
160 | |||
161 | /** | ||
162 | * Hash of the contents of the file. | ||
163 | */ | ||
164 | struct GNUNET_HashCode file_id; | ||
165 | }; | ||
166 | |||
167 | |||
168 | /** | ||
169 | * A local client. | ||
170 | */ | ||
171 | struct GSF_LocalClient | ||
172 | { | ||
173 | /** | ||
174 | * ID of the client. | ||
175 | */ | ||
176 | struct GNUNET_SERVICE_Client *client; | ||
177 | |||
178 | /** | ||
179 | * Queue for sending replies. | ||
180 | */ | ||
181 | struct GNUNET_MQ_Handle *mq; | ||
182 | |||
183 | /** | ||
184 | * Head of list of requests performed on behalf | ||
185 | * of this client right now. | ||
186 | */ | ||
187 | struct ClientRequest *cr_head; | ||
188 | |||
189 | /** | ||
190 | * Tail of list of requests performed on behalf | ||
191 | * of this client right now. | ||
192 | */ | ||
193 | struct ClientRequest *cr_tail; | ||
194 | |||
195 | /** | ||
196 | * This is a doubly linked list. | ||
197 | */ | ||
198 | struct IndexStartContext *isc_head; | ||
199 | |||
200 | /** | ||
201 | * This is a doubly linked list. | ||
202 | */ | ||
203 | struct IndexStartContext *isc_tail; | ||
204 | |||
205 | /** | ||
206 | * Head of linked list of responses. | ||
207 | */ | ||
208 | struct ClientResponse *res_head; | ||
209 | |||
210 | /** | ||
211 | * Tail of linked list of responses. | ||
212 | */ | ||
213 | struct ClientResponse *res_tail; | ||
214 | }; | ||
215 | |||
216 | |||
217 | /* ****************************** globals ****************************** */ | ||
218 | |||
219 | /** | ||
220 | * Our connection to the datastore. | ||
221 | */ | ||
222 | struct GNUNET_DATASTORE_Handle *GSF_dsh; | ||
223 | |||
224 | /** | ||
225 | * Our configuration. | ||
226 | */ | ||
227 | const struct GNUNET_CONFIGURATION_Handle *GSF_cfg; | ||
228 | |||
229 | /** | ||
230 | * Handle for reporting statistics. | ||
231 | */ | ||
232 | struct GNUNET_STATISTICS_Handle *GSF_stats; | ||
233 | |||
234 | /** | ||
235 | * Handle for DHT operations. | ||
236 | */ | ||
237 | struct GNUNET_DHT_Handle *GSF_dht; | ||
238 | |||
239 | /** | ||
240 | * How long do requests typically stay in the routing table? | ||
241 | */ | ||
242 | struct GNUNET_LOAD_Value *GSF_rt_entry_lifetime; | ||
243 | |||
244 | /** | ||
245 | * Running average of the observed latency to other peers (round trip). | ||
246 | * Initialized to 5s as the initial default. | ||
247 | */ | ||
248 | struct GNUNET_TIME_Relative GSF_avg_latency = { 500 }; | ||
249 | |||
250 | |||
251 | /** | ||
252 | * Typical priorities we're seeing from other peers right now. Since | ||
253 | * most priorities will be zero, this value is the weighted average of | ||
254 | * non-zero priorities seen "recently". In order to ensure that new | ||
255 | * values do not dramatically change the ratio, values are first | ||
256 | * "capped" to a reasonable range (+N of the current value) and then | ||
257 | * averaged into the existing value by a ratio of 1:N. Hence | ||
258 | * receiving the largest possible priority can still only raise our | ||
259 | * "current_priorities" by at most 1. | ||
260 | */ | ||
261 | double GSF_current_priorities; | ||
262 | |||
263 | /** | ||
264 | * Size of the datastore queue we assume for common requests. | ||
265 | */ | ||
266 | unsigned int GSF_datastore_queue_size; | ||
267 | |||
268 | /** | ||
269 | * How many query messages have we received 'recently' that | ||
270 | * have not yet been claimed as cover traffic? | ||
271 | */ | ||
272 | unsigned int GSF_cover_query_count; | ||
273 | |||
274 | /** | ||
275 | * How many content messages have we received 'recently' that | ||
276 | * have not yet been claimed as cover traffic? | ||
277 | */ | ||
278 | unsigned int GSF_cover_content_count; | ||
279 | |||
280 | /** | ||
281 | * Our block context. | ||
282 | */ | ||
283 | struct GNUNET_BLOCK_Context *GSF_block_ctx; | ||
284 | |||
285 | /** | ||
286 | * Pointer to handle to the core service (points to NULL until we've | ||
287 | * connected to it). | ||
288 | */ | ||
289 | struct GNUNET_CORE_Handle *GSF_core; | ||
290 | |||
291 | /** | ||
292 | * Are we introducing randomized delays for better anonymity? | ||
293 | */ | ||
294 | int GSF_enable_randomized_delays; | ||
295 | |||
296 | /** | ||
297 | * Identity of this peer. | ||
298 | */ | ||
299 | struct GNUNET_PeerIdentity GSF_my_id; | ||
300 | |||
301 | /* ***************************** locals ******************************* */ | ||
302 | |||
303 | /** | ||
304 | * Configuration for block library. | ||
305 | */ | ||
306 | static struct GNUNET_CONFIGURATION_Handle *block_cfg; | ||
307 | |||
308 | /** | ||
309 | * Private key of this peer. Used to sign LOC URI requests. | ||
310 | */ | ||
311 | static struct GNUNET_CRYPTO_EddsaPrivateKey pk; | ||
312 | |||
313 | /** | ||
314 | * ID of our task that we use to age the cover counters. | ||
315 | */ | ||
316 | static struct GNUNET_SCHEDULER_Task *cover_age_task; | ||
317 | |||
318 | /** | ||
319 | * Datastore 'GET' load tracking. | ||
320 | */ | ||
321 | static struct GNUNET_LOAD_Value *datastore_get_load; | ||
322 | |||
323 | |||
324 | /** | ||
325 | * Creates a fresh local client handle. | ||
326 | * | ||
327 | * @param cls NULL | ||
328 | * @param client handle of the client | ||
329 | * @param mq message queue for @a client | ||
330 | * @return handle to local client entry | ||
331 | */ | ||
332 | static void * | ||
333 | client_connect_cb (void *cls, | ||
334 | struct GNUNET_SERVICE_Client *client, | ||
335 | struct GNUNET_MQ_Handle *mq) | ||
336 | { | ||
337 | struct GSF_LocalClient *pos; | ||
338 | |||
339 | pos = GNUNET_new (struct GSF_LocalClient); | ||
340 | pos->client = client; | ||
341 | pos->mq = mq; | ||
342 | return pos; | ||
343 | } | ||
344 | |||
345 | |||
346 | /** | ||
347 | * Free the given client request. | ||
348 | * | ||
349 | * @param cls the client request to free | ||
350 | */ | ||
351 | static void | ||
352 | client_request_destroy (void *cls) | ||
353 | { | ||
354 | struct ClientRequest *cr = cls; | ||
355 | struct GSF_LocalClient *lc = cr->lc; | ||
356 | |||
357 | cr->kill_task = NULL; | ||
358 | GNUNET_CONTAINER_DLL_remove (lc->cr_head, | ||
359 | lc->cr_tail, | ||
360 | cr); | ||
361 | GSF_pending_request_cancel_ (cr->pr, | ||
362 | GNUNET_YES); | ||
363 | GNUNET_STATISTICS_update (GSF_stats, | ||
364 | gettext_noop ("# client searches active"), | ||
365 | -1, | ||
366 | GNUNET_NO); | ||
367 | GNUNET_free (cr); | ||
368 | } | ||
369 | |||
370 | |||
371 | /** | ||
372 | * Handle a reply to a pending request. Also called if a request | ||
373 | * expires (then with data == NULL). The handler may be called | ||
374 | * many times (depending on the request type), but will not be | ||
375 | * called during or after a call to #GSF_pending_request_cancel() | ||
376 | * and will also not be called anymore after a call signalling | ||
377 | * expiration. | ||
378 | * | ||
379 | * @param cls user-specified closure | ||
380 | * @param eval evaluation of the result | ||
381 | * @param pr handle to the original pending request | ||
382 | * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown" | ||
383 | * @param expiration when does @a data expire? | ||
384 | * @param last_transmission when was the last time we've tried to download this block? (FOREVER if unknown) | ||
385 | * @param type type of the block | ||
386 | * @param data response data, NULL on request expiration | ||
387 | * @param data_len number of bytes in @a data | ||
388 | */ | ||
389 | static void | ||
390 | client_response_handler (void *cls, | ||
391 | enum GNUNET_BLOCK_ReplyEvaluationResult eval, | ||
392 | struct GSF_PendingRequest *pr, | ||
393 | uint32_t reply_anonymity_level, | ||
394 | struct GNUNET_TIME_Absolute expiration, | ||
395 | struct GNUNET_TIME_Absolute last_transmission, | ||
396 | enum GNUNET_BLOCK_Type type, | ||
397 | const void *data, | ||
398 | size_t data_len) | ||
399 | { | ||
400 | struct ClientRequest *cr = cls; | ||
401 | struct GSF_LocalClient *lc; | ||
402 | struct GNUNET_MQ_Envelope *env; | ||
403 | struct ClientPutMessage *pm; | ||
404 | const struct GSF_PendingRequestData *prd; | ||
405 | |||
406 | if (NULL == data) | ||
407 | { | ||
408 | /* local-only request, with no result, clean up. */ | ||
409 | if (NULL == cr->kill_task) | ||
410 | cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy, | ||
411 | cr); | ||
412 | return; | ||
413 | } | ||
414 | prd = GSF_pending_request_get_data_ (pr); | ||
415 | GNUNET_break (type != GNUNET_BLOCK_TYPE_ANY); | ||
416 | if ((prd->type != type) && (prd->type != GNUNET_BLOCK_TYPE_ANY)) | ||
417 | { | ||
418 | GNUNET_break (0); | ||
419 | return; | ||
420 | } | ||
421 | GNUNET_STATISTICS_update (GSF_stats, | ||
422 | gettext_noop | ||
423 | ("# replies received for local clients"), 1, | ||
424 | GNUNET_NO); | ||
425 | GNUNET_assert (pr == cr->pr); | ||
426 | lc = cr->lc; | ||
427 | env = GNUNET_MQ_msg_extra (pm, | ||
428 | data_len, | ||
429 | GNUNET_MESSAGE_TYPE_FS_PUT); | ||
430 | pm->type = htonl (type); | ||
431 | pm->expiration = GNUNET_TIME_absolute_hton (expiration); | ||
432 | pm->last_transmission = GNUNET_TIME_absolute_hton (last_transmission); | ||
433 | pm->num_transmissions = htonl (prd->num_transmissions); | ||
434 | pm->respect_offered = htonl (prd->respect_offered); | ||
435 | GNUNET_memcpy (&pm[1], | ||
436 | data, | ||
437 | data_len); | ||
438 | GNUNET_MQ_send (lc->mq, | ||
439 | env); | ||
440 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
441 | "Queued reply to query `%s' for local client\n", | ||
442 | GNUNET_h2s (&prd->query)); | ||
443 | if (GNUNET_BLOCK_REPLY_OK_LAST != eval) | ||
444 | { | ||
445 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
446 | "Evaluation %d - keeping query alive\n", | ||
447 | (int) eval); | ||
448 | return; | ||
449 | } | ||
450 | if (NULL == cr->kill_task) | ||
451 | cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy, | ||
452 | cr); | ||
453 | } | ||
454 | |||
455 | |||
456 | /** | ||
457 | * A client disconnected from us. Tear down the local client | ||
458 | * record. | ||
459 | * | ||
460 | * @param cls unused | ||
461 | * @param client handle of the client | ||
462 | * @param app_ctx the `struct GSF_LocalClient` | ||
463 | */ | ||
464 | static void | ||
465 | client_disconnect_cb (void *cls, | ||
466 | struct GNUNET_SERVICE_Client *client, | ||
467 | void *app_ctx) | ||
468 | { | ||
469 | struct GSF_LocalClient *lc = app_ctx; | ||
470 | struct IndexStartContext *isc; | ||
471 | struct ClientRequest *cr; | ||
472 | struct ClientResponse *res; | ||
473 | |||
474 | while (NULL != (cr = lc->cr_head)) | ||
475 | { | ||
476 | if (NULL != cr->kill_task) | ||
477 | GNUNET_SCHEDULER_cancel (cr->kill_task); | ||
478 | client_request_destroy (cr); | ||
479 | } | ||
480 | while (NULL != (res = lc->res_head)) | ||
481 | { | ||
482 | GNUNET_CONTAINER_DLL_remove (lc->res_head, | ||
483 | lc->res_tail, | ||
484 | res); | ||
485 | GNUNET_free (res); | ||
486 | } | ||
487 | while (NULL != (isc = lc->isc_head)) | ||
488 | { | ||
489 | GNUNET_CONTAINER_DLL_remove (lc->isc_head, | ||
490 | lc->isc_tail, | ||
491 | isc); | ||
492 | GNUNET_CRYPTO_hash_file_cancel (isc->fhc); | ||
493 | GNUNET_free (isc); | ||
494 | } | ||
495 | GNUNET_free (lc); | ||
496 | } | ||
497 | |||
498 | |||
499 | /** | ||
500 | * Task that periodically ages our cover traffic statistics. | ||
501 | * | ||
502 | * @param cls unused closure | ||
503 | */ | ||
504 | static void | ||
505 | age_cover_counters (void *cls) | ||
506 | { | ||
507 | GSF_cover_content_count = (GSF_cover_content_count * 15) / 16; | ||
508 | GSF_cover_query_count = (GSF_cover_query_count * 15) / 16; | ||
509 | cover_age_task = | ||
510 | GNUNET_SCHEDULER_add_delayed (COVER_AGE_FREQUENCY, | ||
511 | &age_cover_counters, | ||
512 | NULL); | ||
513 | } | ||
514 | |||
515 | |||
516 | /** | ||
517 | * We've just now completed a datastore request. Update our | ||
518 | * datastore load calculations. | ||
519 | * | ||
520 | * @param start time when the datastore request was issued | ||
521 | */ | ||
522 | void | ||
523 | GSF_update_datastore_delay_ (struct GNUNET_TIME_Absolute start) | ||
524 | { | ||
525 | struct GNUNET_TIME_Relative delay; | ||
526 | |||
527 | delay = GNUNET_TIME_absolute_get_duration (start); | ||
528 | GNUNET_LOAD_update (datastore_get_load, delay.rel_value_us); | ||
529 | } | ||
530 | |||
531 | |||
532 | /** | ||
533 | * Test if the DATABASE (GET) load on this peer is too high | ||
534 | * to even consider processing the query at | ||
535 | * all. | ||
536 | * | ||
537 | * @param priority priority of the request (used as a reference point to compare with the load) | ||
538 | * @return #GNUNET_YES if the load is too high to do anything (load high) | ||
539 | * #GNUNET_NO to process normally (load normal) | ||
540 | * #GNUNET_SYSERR to process for free (load low) | ||
541 | */ | ||
542 | int | ||
543 | GSF_test_get_load_too_high_ (uint32_t priority) | ||
544 | { | ||
545 | double ld; | ||
546 | |||
547 | ld = GNUNET_LOAD_get_load (datastore_get_load); | ||
548 | if (ld < 1) | ||
549 | return GNUNET_SYSERR; | ||
550 | if (ld <= priority) | ||
551 | return GNUNET_NO; | ||
552 | return GNUNET_YES; | ||
553 | } | ||
554 | |||
555 | |||
556 | /** | ||
557 | * Check P2P "PUT" message. | ||
558 | * | ||
559 | * @param cls closure with the `struct GSF_ConnectedPeer` | ||
560 | * @param put the actual message | ||
561 | * @return #GNUNET_OK to keep the connection open, | ||
562 | * #GNUNET_SYSERR to close it (signal serious error) | ||
563 | */ | ||
564 | static int | ||
565 | check_p2p_put (void *cls, | ||
566 | const struct PutMessage *put) | ||
567 | { | ||
568 | enum GNUNET_BLOCK_Type type; | ||
569 | |||
570 | type = ntohl (put->type); | ||
571 | if (GNUNET_BLOCK_TYPE_FS_ONDEMAND == type) | ||
572 | { | ||
573 | GNUNET_break_op (0); | ||
574 | return GNUNET_SYSERR; | ||
575 | } | ||
576 | return GNUNET_OK; | ||
577 | } | ||
578 | |||
579 | |||
580 | /** | ||
581 | * We have a new request, consider forwarding it to the given | ||
582 | * peer. | ||
583 | * | ||
584 | * @param cls the `struct GSF_PendingRequest` | ||
585 | * @param peer identity of the peer | ||
586 | * @param cp handle to the connected peer record | ||
587 | * @param ppd peer performance data | ||
588 | */ | ||
589 | static void | ||
590 | consider_request_for_forwarding (void *cls, | ||
591 | const struct GNUNET_PeerIdentity *peer, | ||
592 | struct GSF_ConnectedPeer *cp, | ||
593 | const struct GSF_PeerPerformanceData *ppd) | ||
594 | { | ||
595 | struct GSF_PendingRequest *pr = cls; | ||
596 | |||
597 | if (GNUNET_YES != | ||
598 | GSF_pending_request_test_target_ (pr, peer)) | ||
599 | { | ||
600 | #if INSANE_STATISTICS | ||
601 | GNUNET_STATISTICS_update (GSF_stats, | ||
602 | gettext_noop ("# Loopback routes suppressed"), 1, | ||
603 | GNUNET_NO); | ||
604 | #endif | ||
605 | return; | ||
606 | } | ||
607 | GSF_plan_add_ (cp, | ||
608 | pr); | ||
609 | } | ||
610 | |||
611 | |||
612 | /** | ||
613 | * Function to be called after we're done processing | ||
614 | * replies from the local lookup. If the result status | ||
615 | * code indicates that there may be more replies, plan | ||
616 | * forwarding the request. | ||
617 | * | ||
618 | * @param cls closure (NULL) | ||
619 | * @param pr the pending request we were processing | ||
620 | * @param result final datastore lookup result | ||
621 | */ | ||
622 | void | ||
623 | GSF_consider_forwarding (void *cls, | ||
624 | struct GSF_PendingRequest *pr, | ||
625 | enum GNUNET_BLOCK_ReplyEvaluationResult result) | ||
626 | { | ||
627 | if (GNUNET_BLOCK_REPLY_OK_LAST == result) | ||
628 | return; /* we're done... */ | ||
629 | if (GNUNET_YES != | ||
630 | GSF_pending_request_test_active_ (pr)) | ||
631 | return; /* request is not actually active, skip! */ | ||
632 | GSF_iterate_connected_peers_ (&consider_request_for_forwarding, | ||
633 | pr); | ||
634 | } | ||
635 | |||
636 | |||
637 | /** | ||
638 | * Check P2P "GET" request. | ||
639 | * | ||
640 | * @param cls closure | ||
641 | * @param gm the actual message | ||
642 | * @return #GNUNET_OK to keep the connection open, | ||
643 | * #GNUNET_SYSERR to close it (signal serious error) | ||
644 | */ | ||
645 | static int | ||
646 | check_p2p_get (void *cls, | ||
647 | const struct GetMessage *gm) | ||
648 | { | ||
649 | size_t msize; | ||
650 | unsigned int bm; | ||
651 | unsigned int bits; | ||
652 | size_t bfsize; | ||
653 | |||
654 | msize = ntohs (gm->header.size); | ||
655 | bm = ntohl (gm->hash_bitmap); | ||
656 | bits = 0; | ||
657 | while (bm > 0) | ||
658 | { | ||
659 | if (1 == (bm & 1)) | ||
660 | bits++; | ||
661 | bm >>= 1; | ||
662 | } | ||
663 | if (msize < sizeof(struct GetMessage) + bits * sizeof(struct | ||
664 | GNUNET_PeerIdentity)) | ||
665 | { | ||
666 | GNUNET_break_op (0); | ||
667 | return GNUNET_SYSERR; | ||
668 | } | ||
669 | bfsize = msize - sizeof(struct GetMessage) - bits * sizeof(struct | ||
670 | GNUNET_PeerIdentity); | ||
671 | /* bfsize must be power of 2, check! */ | ||
672 | if (0 != ((bfsize - 1) & bfsize)) | ||
673 | { | ||
674 | GNUNET_break_op (0); | ||
675 | return GNUNET_SYSERR; | ||
676 | } | ||
677 | return GNUNET_OK; | ||
678 | } | ||
679 | |||
680 | |||
681 | /** | ||
682 | * We're done with the local lookup, now consider | ||
683 | * P2P processing (depending on request options and | ||
684 | * result status). Also signal that we can now | ||
685 | * receive more request information from the client. | ||
686 | * | ||
687 | * @param cls the client doing the request (`struct GSF_LocalClient`) | ||
688 | * @param pr the pending request we were processing | ||
689 | * @param result final datastore lookup result | ||
690 | */ | ||
691 | static void | ||
692 | start_p2p_processing (void *cls, | ||
693 | struct GSF_PendingRequest *pr, | ||
694 | enum GNUNET_BLOCK_ReplyEvaluationResult result) | ||
695 | { | ||
696 | struct GSF_LocalClient *lc = cls; | ||
697 | struct GSF_PendingRequestData *prd; | ||
698 | |||
699 | GNUNET_SERVICE_client_continue (lc->client); | ||
700 | if (GNUNET_BLOCK_REPLY_OK_LAST == result) | ||
701 | return; /* we're done, 'pr' was already destroyed... */ | ||
702 | prd = GSF_pending_request_get_data_ (pr); | ||
703 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
704 | "Finished database lookup for local request `%s' with result %d\n", | ||
705 | GNUNET_h2s (&prd->query), | ||
706 | result); | ||
707 | if (0 == prd->anonymity_level) | ||
708 | { | ||
709 | switch (prd->type) | ||
710 | { | ||
711 | case GNUNET_BLOCK_TYPE_FS_DBLOCK: | ||
712 | case GNUNET_BLOCK_TYPE_FS_IBLOCK: | ||
713 | /* the above block types MAY be available via 'cadet' */ | ||
714 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
715 | "Considering cadet-based download for block\n"); | ||
716 | GSF_cadet_lookup_ (pr); | ||
717 | break; | ||
718 | |||
719 | case GNUNET_BLOCK_TYPE_FS_UBLOCK: | ||
720 | /* the above block types are in the DHT */ | ||
721 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
722 | "Considering DHT-based search for block\n"); | ||
723 | GSF_dht_lookup_ (pr); | ||
724 | break; | ||
725 | |||
726 | default: | ||
727 | GNUNET_break (0); | ||
728 | break; | ||
729 | } | ||
730 | } | ||
731 | GSF_consider_forwarding (NULL, | ||
732 | pr, | ||
733 | result); | ||
734 | } | ||
735 | |||
736 | |||
737 | /** | ||
738 | * Check #GNUNET_MESSAGE_TYPE_FS_START_SEARCH-message (search request | ||
739 | * from client). | ||
740 | * | ||
741 | * @param cls identification of the client | ||
742 | * @param sm the actual message | ||
743 | * @return #GNUNET_OK if @a sm is well-formed | ||
744 | */ | ||
745 | static int | ||
746 | check_client_start_search (void *cls, | ||
747 | const struct SearchMessage *sm) | ||
748 | { | ||
749 | uint16_t msize; | ||
750 | |||
751 | msize = ntohs (sm->header.size) - sizeof(struct SearchMessage); | ||
752 | if (0 != msize % sizeof(struct GNUNET_HashCode)) | ||
753 | { | ||
754 | GNUNET_break (0); | ||
755 | return GNUNET_SYSERR; | ||
756 | } | ||
757 | return GNUNET_OK; | ||
758 | } | ||
759 | |||
760 | |||
761 | /** | ||
762 | * Handle #GNUNET_MESSAGE_TYPE_FS_START_SEARCH-message (search request | ||
763 | * from client). | ||
764 | * | ||
765 | * Responsible for creating the request entry itself and setting | ||
766 | * up reply callback and cancellation on client disconnect. | ||
767 | * | ||
768 | * @param cls identification of the client | ||
769 | * @param sm the actual message | ||
770 | */ | ||
771 | static void | ||
772 | handle_client_start_search (void *cls, | ||
773 | const struct SearchMessage *sm) | ||
774 | { | ||
775 | static struct GNUNET_PeerIdentity all_zeros; | ||
776 | struct GSF_LocalClient *lc = cls; | ||
777 | struct ClientRequest *cr; | ||
778 | struct GSF_PendingRequestData *prd; | ||
779 | uint16_t msize; | ||
780 | unsigned int sc; | ||
781 | enum GNUNET_BLOCK_Type type; | ||
782 | enum GSF_PendingRequestOptions options; | ||
783 | |||
784 | GNUNET_STATISTICS_update (GSF_stats, | ||
785 | gettext_noop ("# client searches received"), | ||
786 | 1, | ||
787 | GNUNET_NO); | ||
788 | msize = ntohs (sm->header.size) - sizeof(struct SearchMessage); | ||
789 | sc = msize / sizeof(struct GNUNET_HashCode); | ||
790 | type = ntohl (sm->type); | ||
791 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
792 | "Received request for `%s' of type %u from local client\n", | ||
793 | GNUNET_h2s (&sm->query), | ||
794 | (unsigned int) type); | ||
795 | cr = NULL; | ||
796 | /* detect duplicate UBLOCK requests */ | ||
797 | if ((type == GNUNET_BLOCK_TYPE_FS_UBLOCK) || | ||
798 | (type == GNUNET_BLOCK_TYPE_ANY)) | ||
799 | { | ||
800 | cr = lc->cr_head; | ||
801 | while (NULL != cr) | ||
802 | { | ||
803 | prd = GSF_pending_request_get_data_ (cr->pr); | ||
804 | /* only unify with queries that hae not yet started local processing | ||
805 | (SEARCH_MESSAGE_OPTION_CONTINUED was always set) and that have a | ||
806 | matching query and type */ | ||
807 | if ((GNUNET_YES != prd->has_started) && | ||
808 | (0 != memcmp (&prd->query, | ||
809 | &sm->query, | ||
810 | sizeof(struct GNUNET_HashCode))) && | ||
811 | (prd->type == type)) | ||
812 | break; | ||
813 | cr = cr->next; | ||
814 | } | ||
815 | } | ||
816 | if (NULL != cr) | ||
817 | { | ||
818 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
819 | "Have existing request, merging content-seen lists.\n"); | ||
820 | GSF_pending_request_update_ (cr->pr, | ||
821 | (const struct GNUNET_HashCode *) &sm[1], | ||
822 | sc); | ||
823 | GNUNET_STATISTICS_update (GSF_stats, | ||
824 | gettext_noop ( | ||
825 | "# client searches updated (merged content seen list)"), | ||
826 | 1, | ||
827 | GNUNET_NO); | ||
828 | } | ||
829 | else | ||
830 | { | ||
831 | GNUNET_STATISTICS_update (GSF_stats, | ||
832 | gettext_noop ("# client searches active"), | ||
833 | 1, | ||
834 | GNUNET_NO); | ||
835 | cr = GNUNET_new (struct ClientRequest); | ||
836 | cr->lc = lc; | ||
837 | GNUNET_CONTAINER_DLL_insert (lc->cr_head, | ||
838 | lc->cr_tail, | ||
839 | cr); | ||
840 | options = GSF_PRO_LOCAL_REQUEST; | ||
841 | if (0 != (SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY & ntohl (sm->options))) | ||
842 | options |= GSF_PRO_LOCAL_ONLY; | ||
843 | cr->pr = GSF_pending_request_create_ (options, type, | ||
844 | &sm->query, | ||
845 | (0 != | ||
846 | memcmp (&sm->target, | ||
847 | &all_zeros, | ||
848 | sizeof(struct | ||
849 | GNUNET_PeerIdentity))) | ||
850 | ? &sm->target : NULL, NULL, | ||
851 | 0 /* bf */, | ||
852 | ntohl (sm->anonymity_level), | ||
853 | 0 /* priority */, | ||
854 | 0 /* ttl */, | ||
855 | 0 /* sender PID */, | ||
856 | 0 /* origin PID */, | ||
857 | (const struct | ||
858 | GNUNET_HashCode *) &sm[1], sc, | ||
859 | &client_response_handler, | ||
860 | cr); | ||
861 | } | ||
862 | if (0 != (SEARCH_MESSAGE_OPTION_CONTINUED & ntohl (sm->options))) | ||
863 | { | ||
864 | GNUNET_SERVICE_client_continue (lc->client); | ||
865 | return; | ||
866 | } | ||
867 | GSF_pending_request_get_data_ (cr->pr)->has_started = GNUNET_YES; | ||
868 | GSF_local_lookup_ (cr->pr, | ||
869 | &start_p2p_processing, | ||
870 | lc); | ||
871 | } | ||
872 | |||
873 | |||
874 | /** | ||
875 | * Handle request to sign a LOC URI (from client). | ||
876 | * | ||
877 | * @param cls identification of the client | ||
878 | * @param msg the actual message | ||
879 | */ | ||
880 | static void | ||
881 | handle_client_loc_sign (void *cls, | ||
882 | const struct RequestLocSignatureMessage *msg) | ||
883 | { | ||
884 | struct GSF_LocalClient *lc = cls; | ||
885 | struct GNUNET_FS_Uri base; | ||
886 | struct GNUNET_FS_Uri *loc; | ||
887 | struct GNUNET_MQ_Envelope *env; | ||
888 | struct ResponseLocSignatureMessage *resp; | ||
889 | |||
890 | GNUNET_break (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT == | ||
891 | ntohl (msg->purpose)); | ||
892 | base.type = GNUNET_FS_URI_CHK; | ||
893 | base.data.chk.chk = msg->chk; | ||
894 | base.data.chk.file_length = GNUNET_ntohll (msg->file_length); | ||
895 | loc = GNUNET_FS_uri_loc_create (&base, | ||
896 | &pk, | ||
897 | GNUNET_TIME_absolute_ntoh ( | ||
898 | msg->expiration_time)); | ||
899 | env = GNUNET_MQ_msg (resp, | ||
900 | GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGNATURE); | ||
901 | resp->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT); | ||
902 | resp->expiration_time = GNUNET_TIME_absolute_hton ( | ||
903 | loc->data.loc.expirationTime); | ||
904 | resp->signature = loc->data.loc.contentSignature; | ||
905 | resp->peer = loc->data.loc.peer; | ||
906 | GNUNET_FS_uri_destroy (loc); | ||
907 | GNUNET_MQ_send (lc->mq, | ||
908 | env); | ||
909 | GNUNET_SERVICE_client_continue (lc->client); | ||
910 | } | ||
911 | |||
912 | |||
913 | /** | ||
914 | * Check INDEX_START-message. | ||
915 | * | ||
916 | * @param cls identification of the client | ||
917 | * @param ism the actual message | ||
918 | * @return #GNUNET_OK if @a ism is well-formed | ||
919 | */ | ||
920 | static int | ||
921 | check_client_index_start (void *cls, | ||
922 | const struct IndexStartMessage *ism) | ||
923 | { | ||
924 | char *fn; | ||
925 | |||
926 | GNUNET_MQ_check_zero_termination (ism); | ||
927 | if (0 != ism->reserved) | ||
928 | { | ||
929 | GNUNET_break (0); | ||
930 | return GNUNET_SYSERR; | ||
931 | } | ||
932 | fn = GNUNET_STRINGS_filename_expand ((const char *) &ism[1]); | ||
933 | if (NULL == fn) | ||
934 | { | ||
935 | GNUNET_break (0); | ||
936 | return GNUNET_SYSERR; | ||
937 | } | ||
938 | GNUNET_free (fn); | ||
939 | return GNUNET_OK; | ||
940 | } | ||
941 | |||
942 | |||
943 | /** | ||
944 | * We've validated the hash of the file we're about to index. Signal | ||
945 | * success to the client and update our internal data structures. | ||
946 | * | ||
947 | * @param isc the data about the index info entry for the request | ||
948 | */ | ||
949 | static void | ||
950 | signal_index_ok (struct IndexStartContext *isc) | ||
951 | { | ||
952 | struct GSF_LocalClient *lc = isc->lc; | ||
953 | struct GNUNET_MQ_Envelope *env; | ||
954 | struct GNUNET_MessageHeader *msg; | ||
955 | |||
956 | GNUNET_FS_add_to_index (isc->filename, | ||
957 | &isc->file_id); | ||
958 | env = GNUNET_MQ_msg (msg, | ||
959 | GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK); | ||
960 | GNUNET_MQ_send (lc->mq, | ||
961 | env); | ||
962 | GNUNET_free (isc->filename); | ||
963 | GNUNET_free (isc); | ||
964 | GNUNET_SERVICE_client_continue (lc->client); | ||
965 | } | ||
966 | |||
967 | |||
968 | /** | ||
969 | * Function called once the hash computation over an | ||
970 | * indexed file has completed. | ||
971 | * | ||
972 | * @param cls closure, our publishing context | ||
973 | * @param res resulting hash, NULL on error | ||
974 | */ | ||
975 | static void | ||
976 | hash_for_index_val (void *cls, | ||
977 | const struct GNUNET_HashCode *res) | ||
978 | { | ||
979 | struct IndexStartContext *isc = cls; | ||
980 | struct GSF_LocalClient *lc = isc->lc; | ||
981 | struct GNUNET_MQ_Envelope *env; | ||
982 | struct GNUNET_MessageHeader *msg; | ||
983 | |||
984 | GNUNET_CONTAINER_DLL_remove (lc->isc_head, | ||
985 | lc->isc_tail, | ||
986 | isc); | ||
987 | isc->fhc = NULL; | ||
988 | if ((NULL == res) || | ||
989 | (0 != memcmp (res, | ||
990 | &isc->file_id, | ||
991 | sizeof(struct GNUNET_HashCode)))) | ||
992 | { | ||
993 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
994 | _ ( | ||
995 | "Hash mismatch trying to index file `%s' which does not have hash `%s'\n"), | ||
996 | isc->filename, | ||
997 | GNUNET_h2s (&isc->file_id)); | ||
998 | |||
999 | const char *emsg = "hash mismatch"; | ||
1000 | const size_t msize = strlen (emsg) + 1; | ||
1001 | |||
1002 | env = GNUNET_MQ_msg_extra (msg, | ||
1003 | msize, | ||
1004 | GNUNET_MESSAGE_TYPE_FS_INDEX_START_FAILED); | ||
1005 | memcpy ((char*) &msg[1], emsg, msize); | ||
1006 | GNUNET_MQ_send (lc->mq, | ||
1007 | env); | ||
1008 | GNUNET_SERVICE_client_continue (lc->client); | ||
1009 | GNUNET_free (isc); | ||
1010 | return; | ||
1011 | } | ||
1012 | signal_index_ok (isc); | ||
1013 | } | ||
1014 | |||
1015 | |||
1016 | /** | ||
1017 | * Handle INDEX_START-message. | ||
1018 | * | ||
1019 | * @param cls identification of the client | ||
1020 | * @param ism the actual message | ||
1021 | */ | ||
1022 | static void | ||
1023 | handle_client_index_start (void *cls, | ||
1024 | const struct IndexStartMessage *ism) | ||
1025 | { | ||
1026 | struct GSF_LocalClient *lc = cls; | ||
1027 | struct IndexStartContext *isc; | ||
1028 | char *fn; | ||
1029 | uint64_t dev; | ||
1030 | uint64_t ino; | ||
1031 | uint64_t mydev; | ||
1032 | uint64_t myino; | ||
1033 | |||
1034 | fn = GNUNET_STRINGS_filename_expand ((const char *) &ism[1]); | ||
1035 | GNUNET_assert (NULL != fn); | ||
1036 | dev = GNUNET_ntohll (ism->device); | ||
1037 | ino = GNUNET_ntohll (ism->inode); | ||
1038 | isc = GNUNET_new (struct IndexStartContext); | ||
1039 | isc->filename = fn; | ||
1040 | isc->file_id = ism->file_id; | ||
1041 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1042 | "Received START_INDEX message for file `%s'\n", | ||
1043 | isc->filename); | ||
1044 | isc->lc = lc; | ||
1045 | mydev = 0; | ||
1046 | myino = 0; | ||
1047 | if (((dev != 0) || | ||
1048 | (ino != 0)) && | ||
1049 | (GNUNET_OK == GNUNET_DISK_file_get_identifiers (fn, | ||
1050 | &mydev, | ||
1051 | &myino)) && | ||
1052 | (dev == mydev) && | ||
1053 | (ino == myino)) | ||
1054 | { | ||
1055 | /* fast validation OK! */ | ||
1056 | signal_index_ok (isc); | ||
1057 | return; | ||
1058 | } | ||
1059 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1060 | "Mismatch in file identifiers (%llu != %llu or %u != %u), need to hash.\n", | ||
1061 | (unsigned long long) ino, | ||
1062 | (unsigned long long) myino, | ||
1063 | (unsigned int) dev, | ||
1064 | (unsigned int) mydev); | ||
1065 | /* slow validation, need to hash full file (again) */ | ||
1066 | GNUNET_CONTAINER_DLL_insert (lc->isc_head, | ||
1067 | lc->isc_tail, | ||
1068 | isc); | ||
1069 | isc->fhc = GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
1070 | isc->filename, | ||
1071 | HASHING_BLOCKSIZE, | ||
1072 | &hash_for_index_val, | ||
1073 | isc); | ||
1074 | if (NULL == isc->fhc) | ||
1075 | hash_for_index_val (isc, | ||
1076 | NULL); | ||
1077 | } | ||
1078 | |||
1079 | |||
1080 | /** | ||
1081 | * Handle INDEX_LIST_GET-message. | ||
1082 | * | ||
1083 | * @param cls closure | ||
1084 | * @param message the actual message | ||
1085 | */ | ||
1086 | static void | ||
1087 | handle_client_index_list_get (void *cls, | ||
1088 | const struct GNUNET_MessageHeader *message) | ||
1089 | { | ||
1090 | struct GSF_LocalClient *lc = cls; | ||
1091 | |||
1092 | GNUNET_FS_indexing_send_list (lc->mq); | ||
1093 | GNUNET_SERVICE_client_continue (lc->client); | ||
1094 | } | ||
1095 | |||
1096 | |||
1097 | /** | ||
1098 | * Handle UNINDEX-message. | ||
1099 | * | ||
1100 | * @param cls identification of the client | ||
1101 | * @param um the actual message | ||
1102 | */ | ||
1103 | static void | ||
1104 | handle_client_unindex (void *cls, | ||
1105 | const struct UnindexMessage *um) | ||
1106 | { | ||
1107 | struct GSF_LocalClient *lc = cls; | ||
1108 | struct GNUNET_MQ_Envelope *env; | ||
1109 | struct GNUNET_MessageHeader *msg; | ||
1110 | int found; | ||
1111 | |||
1112 | GNUNET_break (0 == um->reserved); | ||
1113 | found = GNUNET_FS_indexing_do_unindex (&um->file_id); | ||
1114 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1115 | "Client requested unindexing of file `%s': %s\n", | ||
1116 | GNUNET_h2s (&um->file_id), | ||
1117 | found ? "found" : "not found"); | ||
1118 | env = GNUNET_MQ_msg (msg, | ||
1119 | GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK); | ||
1120 | GNUNET_MQ_send (lc->mq, | ||
1121 | env); | ||
1122 | GNUNET_SERVICE_client_continue (lc->client); | ||
1123 | } | ||
1124 | |||
1125 | |||
1126 | /** | ||
1127 | * Task run during shutdown. | ||
1128 | * | ||
1129 | * @param cls unused | ||
1130 | */ | ||
1131 | static void | ||
1132 | shutdown_task (void *cls) | ||
1133 | { | ||
1134 | GSF_cadet_stop_server (); | ||
1135 | if (NULL != GSF_core) | ||
1136 | { | ||
1137 | GNUNET_CORE_disconnect (GSF_core); | ||
1138 | GSF_core = NULL; | ||
1139 | } | ||
1140 | GSF_put_done_ (); | ||
1141 | GSF_push_done_ (); | ||
1142 | GSF_pending_request_done_ (); | ||
1143 | GSF_plan_done (); | ||
1144 | GSF_connected_peer_done_ (); | ||
1145 | GNUNET_DATASTORE_disconnect (GSF_dsh, | ||
1146 | GNUNET_NO); | ||
1147 | GSF_dsh = NULL; | ||
1148 | GNUNET_DHT_disconnect (GSF_dht); | ||
1149 | GSF_dht = NULL; | ||
1150 | GNUNET_BLOCK_context_destroy (GSF_block_ctx); | ||
1151 | GSF_block_ctx = NULL; | ||
1152 | GNUNET_CONFIGURATION_destroy (block_cfg); | ||
1153 | block_cfg = NULL; | ||
1154 | GNUNET_STATISTICS_destroy (GSF_stats, GNUNET_NO); | ||
1155 | GSF_stats = NULL; | ||
1156 | if (NULL != cover_age_task) | ||
1157 | { | ||
1158 | GNUNET_SCHEDULER_cancel (cover_age_task); | ||
1159 | cover_age_task = NULL; | ||
1160 | } | ||
1161 | GNUNET_FS_indexing_done (); | ||
1162 | GNUNET_LOAD_value_free (datastore_get_load); | ||
1163 | datastore_get_load = NULL; | ||
1164 | GNUNET_LOAD_value_free (GSF_rt_entry_lifetime); | ||
1165 | GSF_rt_entry_lifetime = NULL; | ||
1166 | } | ||
1167 | |||
1168 | |||
1169 | /** | ||
1170 | * Function called after GNUNET_CORE_connect has succeeded | ||
1171 | * (or failed for good). Note that the private key of the | ||
1172 | * peer is intentionally not exposed here; if you need it, | ||
1173 | * your process should try to read the private key file | ||
1174 | * directly (which should work if you are authorized...). | ||
1175 | * | ||
1176 | * @param cls closure | ||
1177 | * @param my_identity ID of this peer, NULL if we failed | ||
1178 | */ | ||
1179 | static void | ||
1180 | peer_init_handler (void *cls, | ||
1181 | const struct GNUNET_PeerIdentity *my_identity) | ||
1182 | { | ||
1183 | if (0 != GNUNET_memcmp (&GSF_my_id, | ||
1184 | my_identity)) | ||
1185 | { | ||
1186 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1187 | "Peer identity mismatch, refusing to start!\n"); | ||
1188 | GNUNET_SCHEDULER_shutdown (); | ||
1189 | } | ||
1190 | } | ||
1191 | |||
1192 | |||
1193 | /** | ||
1194 | * Process fs requests. | ||
1195 | * | ||
1196 | * @param c configuration to use | ||
1197 | */ | ||
1198 | static int | ||
1199 | main_init (const struct GNUNET_CONFIGURATION_Handle *c) | ||
1200 | { | ||
1201 | struct GNUNET_MQ_MessageHandler no_p2p_handlers[] = { | ||
1202 | GNUNET_MQ_handler_end () | ||
1203 | }; | ||
1204 | struct GNUNET_MQ_MessageHandler p2p_handlers[] = { | ||
1205 | GNUNET_MQ_hd_var_size (p2p_get, | ||
1206 | GNUNET_MESSAGE_TYPE_FS_GET, | ||
1207 | struct GetMessage, | ||
1208 | NULL), | ||
1209 | GNUNET_MQ_hd_var_size (p2p_put, | ||
1210 | GNUNET_MESSAGE_TYPE_FS_PUT, | ||
1211 | struct PutMessage, | ||
1212 | NULL), | ||
1213 | GNUNET_MQ_hd_fixed_size (p2p_migration_stop, | ||
1214 | GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP, | ||
1215 | struct MigrationStopMessage, | ||
1216 | NULL), | ||
1217 | GNUNET_MQ_handler_end () | ||
1218 | }; | ||
1219 | int anon_p2p_off; | ||
1220 | char *keyfile; | ||
1221 | |||
1222 | /* this option is really only for testcases that need to disable | ||
1223 | _anonymous_ file-sharing for some reason */ | ||
1224 | anon_p2p_off = (GNUNET_YES == | ||
1225 | GNUNET_CONFIGURATION_get_value_yesno (GSF_cfg, | ||
1226 | "fs", | ||
1227 | "DISABLE_ANON_TRANSFER")); | ||
1228 | |||
1229 | if (GNUNET_OK != | ||
1230 | GNUNET_CONFIGURATION_get_value_filename (GSF_cfg, | ||
1231 | "PEER", | ||
1232 | "PRIVATE_KEY", | ||
1233 | &keyfile)) | ||
1234 | { | ||
1235 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1236 | _ ( | ||
1237 | "FS service is lacking HOSTKEY configuration setting. Exiting.\n")); | ||
1238 | GNUNET_SCHEDULER_shutdown (); | ||
1239 | return GNUNET_SYSERR; | ||
1240 | } | ||
1241 | if (GNUNET_SYSERR == | ||
1242 | GNUNET_CRYPTO_eddsa_key_from_file (keyfile, | ||
1243 | GNUNET_YES, | ||
1244 | &pk)) | ||
1245 | { | ||
1246 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1247 | "Failed to setup peer's private key\n"); | ||
1248 | GNUNET_SCHEDULER_shutdown (); | ||
1249 | GNUNET_free (keyfile); | ||
1250 | return GNUNET_SYSERR; | ||
1251 | } | ||
1252 | GNUNET_free (keyfile); | ||
1253 | GNUNET_CRYPTO_eddsa_key_get_public (&pk, | ||
1254 | &GSF_my_id.public_key); | ||
1255 | |||
1256 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1257 | "I am peer %s\n", | ||
1258 | GNUNET_i2s (&GSF_my_id)); | ||
1259 | GSF_core | ||
1260 | = GNUNET_CORE_connect (GSF_cfg, | ||
1261 | NULL, | ||
1262 | &peer_init_handler, | ||
1263 | &GSF_peer_connect_handler, | ||
1264 | &GSF_peer_disconnect_handler, | ||
1265 | (GNUNET_YES == anon_p2p_off) | ||
1266 | ? no_p2p_handlers | ||
1267 | : p2p_handlers); | ||
1268 | if (NULL == GSF_core) | ||
1269 | { | ||
1270 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1271 | _ ("Failed to connect to `%s' service.\n"), | ||
1272 | "core"); | ||
1273 | return GNUNET_SYSERR; | ||
1274 | } | ||
1275 | cover_age_task = | ||
1276 | GNUNET_SCHEDULER_add_delayed (COVER_AGE_FREQUENCY, | ||
1277 | &age_cover_counters, | ||
1278 | NULL); | ||
1279 | datastore_get_load = GNUNET_LOAD_value_init (DATASTORE_LOAD_AUTODECLINE); | ||
1280 | GSF_cadet_start_server (); | ||
1281 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
1282 | NULL); | ||
1283 | return GNUNET_OK; | ||
1284 | } | ||
1285 | |||
1286 | |||
1287 | /** | ||
1288 | * Process fs requests. | ||
1289 | * | ||
1290 | * @param cls closure | ||
1291 | * @param cfg configuration to use | ||
1292 | * @param service the initialized service | ||
1293 | */ | ||
1294 | static void | ||
1295 | run (void *cls, | ||
1296 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
1297 | struct GNUNET_SERVICE_Handle *service) | ||
1298 | { | ||
1299 | unsigned long long dqs; | ||
1300 | |||
1301 | GSF_cfg = cfg; | ||
1302 | if (GNUNET_OK != | ||
1303 | GNUNET_CONFIGURATION_get_value_size (GSF_cfg, | ||
1304 | "fs", | ||
1305 | "DATASTORE_QUEUE_SIZE", | ||
1306 | &dqs)) | ||
1307 | { | ||
1308 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, | ||
1309 | "fs", | ||
1310 | "DATASTORE_QUEUE_SIZE"); | ||
1311 | dqs = 32; | ||
1312 | } | ||
1313 | GSF_datastore_queue_size = (unsigned int) dqs; | ||
1314 | GSF_enable_randomized_delays = | ||
1315 | GNUNET_CONFIGURATION_get_value_yesno (cfg, "fs", "DELAY"); | ||
1316 | GSF_dsh = GNUNET_DATASTORE_connect (cfg); | ||
1317 | if (NULL == GSF_dsh) | ||
1318 | { | ||
1319 | GNUNET_SCHEDULER_shutdown (); | ||
1320 | return; | ||
1321 | } | ||
1322 | GSF_rt_entry_lifetime = GNUNET_LOAD_value_init (GNUNET_TIME_UNIT_FOREVER_REL); | ||
1323 | GSF_stats = GNUNET_STATISTICS_create ("fs", cfg); | ||
1324 | block_cfg = GNUNET_CONFIGURATION_create (); | ||
1325 | GSF_block_ctx = GNUNET_BLOCK_context_create (block_cfg); | ||
1326 | GNUNET_assert (NULL != GSF_block_ctx); | ||
1327 | GSF_dht = GNUNET_DHT_connect (cfg, FS_DHT_HT_SIZE); | ||
1328 | GSF_plan_init (); | ||
1329 | GSF_pending_request_init_ (); | ||
1330 | GSF_connected_peer_init_ (); | ||
1331 | |||
1332 | GSF_push_init_ (); | ||
1333 | GSF_put_init_ (); | ||
1334 | if ((GNUNET_OK != GNUNET_FS_indexing_init (cfg, | ||
1335 | GSF_dsh)) || | ||
1336 | (GNUNET_OK != main_init (cfg))) | ||
1337 | { | ||
1338 | GNUNET_SCHEDULER_shutdown (); | ||
1339 | shutdown_task (NULL); | ||
1340 | return; | ||
1341 | } | ||
1342 | } | ||
1343 | |||
1344 | |||
1345 | /** | ||
1346 | * Define "main" method using service macro. | ||
1347 | */ | ||
1348 | GNUNET_SERVICE_MAIN | ||
1349 | ("fs", | ||
1350 | GNUNET_SERVICE_OPTION_NONE, | ||
1351 | &run, | ||
1352 | &client_connect_cb, | ||
1353 | &client_disconnect_cb, | ||
1354 | NULL, | ||
1355 | GNUNET_MQ_hd_var_size (client_index_start, | ||
1356 | GNUNET_MESSAGE_TYPE_FS_INDEX_START, | ||
1357 | struct IndexStartMessage, | ||
1358 | NULL), | ||
1359 | GNUNET_MQ_hd_fixed_size (client_index_list_get, | ||
1360 | GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET, | ||
1361 | struct GNUNET_MessageHeader, | ||
1362 | NULL), | ||
1363 | GNUNET_MQ_hd_fixed_size (client_unindex, | ||
1364 | GNUNET_MESSAGE_TYPE_FS_UNINDEX, | ||
1365 | struct UnindexMessage, | ||
1366 | NULL), | ||
1367 | GNUNET_MQ_hd_var_size (client_start_search, | ||
1368 | GNUNET_MESSAGE_TYPE_FS_START_SEARCH, | ||
1369 | struct SearchMessage, | ||
1370 | NULL), | ||
1371 | GNUNET_MQ_hd_fixed_size (client_loc_sign, | ||
1372 | GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGN, | ||
1373 | struct RequestLocSignatureMessage, | ||
1374 | NULL), | ||
1375 | GNUNET_MQ_handler_end ()); | ||
1376 | |||
1377 | |||
1378 | /* 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 e102a1fba..000000000 --- a/src/fs/gnunet-service-fs.h +++ /dev/null | |||
@@ -1,304 +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_core_service.h" | ||
32 | #include "gnunet_block_lib.h" | ||
33 | #include "fs.h" | ||
34 | |||
35 | |||
36 | /** | ||
37 | * By which amount do we decrement the TTL for simple forwarding / | ||
38 | * indirection of the query; in milli-seconds. Set somewhat in | ||
39 | * accordance to your network latency (above the time it'll take you | ||
40 | * to send a packet and get a reply). | ||
41 | */ | ||
42 | #define TTL_DECREMENT 5000 | ||
43 | |||
44 | /** | ||
45 | * At what frequency should our datastore load decrease | ||
46 | * automatically (since if we don't use it, clearly the | ||
47 | * load must be going down). | ||
48 | */ | ||
49 | #define DATASTORE_LOAD_AUTODECLINE GNUNET_TIME_relative_multiply ( \ | ||
50 | GNUNET_TIME_UNIT_MILLISECONDS, 250) | ||
51 | |||
52 | /** | ||
53 | * Only the (mandatory) query is included. | ||
54 | */ | ||
55 | #define GET_MESSAGE_BIT_QUERY_ONLY 0 | ||
56 | |||
57 | /** | ||
58 | * The peer identity of a peer waiting for the | ||
59 | * reply is included (used if the response | ||
60 | * should be transmitted to someone other than | ||
61 | * the sender of the GET). | ||
62 | */ | ||
63 | #define GET_MESSAGE_BIT_RETURN_TO 1 | ||
64 | |||
65 | /** | ||
66 | * The peer identity of a peer that had claimed to have the content | ||
67 | * previously is included (can be used if responder-anonymity is not | ||
68 | * desired; note that the precursor presumably lacked a direct | ||
69 | * connection to the specified peer; still, the receiver is in no way | ||
70 | * required to limit forwarding only to the specified peer, it should | ||
71 | * only prefer it somewhat if possible). | ||
72 | */ | ||
73 | #define GET_MESSAGE_BIT_TRANSMIT_TO 4 | ||
74 | |||
75 | |||
76 | GNUNET_NETWORK_STRUCT_BEGIN | ||
77 | |||
78 | /** | ||
79 | * Message sent between peers asking for FS-content. | ||
80 | */ | ||
81 | struct GetMessage | ||
82 | { | ||
83 | /** | ||
84 | * Message type will be #GNUNET_MESSAGE_TYPE_FS_GET. | ||
85 | */ | ||
86 | struct GNUNET_MessageHeader header; | ||
87 | |||
88 | /** | ||
89 | * Type of the query (block type). | ||
90 | */ | ||
91 | uint32_t type GNUNET_PACKED; | ||
92 | |||
93 | /** | ||
94 | * How important is this request (network byte order) | ||
95 | */ | ||
96 | uint32_t priority GNUNET_PACKED; | ||
97 | |||
98 | /** | ||
99 | * Relative time to live in MILLISECONDS (network byte order) | ||
100 | */ | ||
101 | int32_t ttl GNUNET_PACKED; | ||
102 | |||
103 | /** | ||
104 | * These days not used. | ||
105 | */ | ||
106 | uint32_t reserved GNUNET_PACKED; | ||
107 | |||
108 | /** | ||
109 | * Which of the optional hash codes are present at the end of the | ||
110 | * message? See GET_MESSAGE_BIT_xx constants. For each bit that is | ||
111 | * set, an additional `struct GNUNET_HashCode` with the respective content | ||
112 | * (in order of the bits) will be appended to the end of the GET | ||
113 | * message. | ||
114 | */ | ||
115 | uint32_t hash_bitmap GNUNET_PACKED; | ||
116 | |||
117 | /** | ||
118 | * Hashcodes of the file(s) we're looking for. | ||
119 | * Details depend on the query type. | ||
120 | */ | ||
121 | struct GNUNET_HashCode query; | ||
122 | |||
123 | /* this is followed by PeerIdentities as specified in the "hash_bitmap"; | ||
124 | * after that, an optional bloomfilter (with bits set for replies | ||
125 | * that should be suppressed) can be present */ | ||
126 | }; | ||
127 | |||
128 | |||
129 | /** | ||
130 | * Message send by a peer that wants to be excluded | ||
131 | * from migration for a while. | ||
132 | */ | ||
133 | struct MigrationStopMessage | ||
134 | { | ||
135 | /** | ||
136 | * Message type will be | ||
137 | * GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP. | ||
138 | */ | ||
139 | struct GNUNET_MessageHeader header; | ||
140 | |||
141 | /** | ||
142 | * Always zero. | ||
143 | */ | ||
144 | uint32_t reserved GNUNET_PACKED; | ||
145 | |||
146 | /** | ||
147 | * How long should the block last? | ||
148 | */ | ||
149 | struct GNUNET_TIME_RelativeNBO duration; | ||
150 | }; | ||
151 | GNUNET_NETWORK_STRUCT_END | ||
152 | |||
153 | /** | ||
154 | * A connected peer. | ||
155 | */ | ||
156 | struct GSF_ConnectedPeer; | ||
157 | |||
158 | /** | ||
159 | * An active request. | ||
160 | */ | ||
161 | struct GSF_PendingRequest; | ||
162 | |||
163 | /** | ||
164 | * A local client. | ||
165 | */ | ||
166 | struct GSF_LocalClient; | ||
167 | |||
168 | /** | ||
169 | * Information kept per plan per request ('pe' module). | ||
170 | */ | ||
171 | struct GSF_RequestPlan; | ||
172 | |||
173 | /** | ||
174 | * Bijection between request plans and pending requests. | ||
175 | */ | ||
176 | struct GSF_PendingRequestPlanBijection; | ||
177 | |||
178 | /** | ||
179 | * Our connection to the datastore. | ||
180 | */ | ||
181 | extern struct GNUNET_DATASTORE_Handle *GSF_dsh; | ||
182 | |||
183 | /** | ||
184 | * Our configuration. | ||
185 | */ | ||
186 | extern const struct GNUNET_CONFIGURATION_Handle *GSF_cfg; | ||
187 | |||
188 | /** | ||
189 | * Handle for reporting statistics. | ||
190 | */ | ||
191 | extern struct GNUNET_STATISTICS_Handle *GSF_stats; | ||
192 | |||
193 | /** | ||
194 | * Pointer to handle to the core service (points to NULL until we've | ||
195 | * connected to it). | ||
196 | */ | ||
197 | extern struct GNUNET_CORE_Handle *GSF_core; | ||
198 | |||
199 | /** | ||
200 | * Handle for DHT operations. | ||
201 | */ | ||
202 | extern struct GNUNET_DHT_Handle *GSF_dht; | ||
203 | |||
204 | /** | ||
205 | * How long do requests typically stay in the routing table? | ||
206 | */ | ||
207 | extern struct GNUNET_LOAD_Value *GSF_rt_entry_lifetime; | ||
208 | |||
209 | /** | ||
210 | * Running average of the observed latency to other peers (round trip). | ||
211 | */ | ||
212 | extern struct GNUNET_TIME_Relative GSF_avg_latency; | ||
213 | |||
214 | /** | ||
215 | * Handle to ATS service. | ||
216 | */ | ||
217 | extern struct GNUNET_ATS_PerformanceHandle *GSF_ats; | ||
218 | |||
219 | /** | ||
220 | * Identity of this peer. | ||
221 | */ | ||
222 | extern struct GNUNET_PeerIdentity GSF_my_id; | ||
223 | |||
224 | /** | ||
225 | * Typical priorities we're seeing from other peers right now. Since | ||
226 | * most priorities will be zero, this value is the weighted average of | ||
227 | * non-zero priorities seen "recently". In order to ensure that new | ||
228 | * values do not dramatically change the ratio, values are first | ||
229 | * "capped" to a reasonable range (+N of the current value) and then | ||
230 | * averaged into the existing value by a ratio of 1:N. Hence | ||
231 | * receiving the largest possible priority can still only raise our | ||
232 | * "current_priorities" by at most 1. | ||
233 | */ | ||
234 | extern double GSF_current_priorities; | ||
235 | |||
236 | /** | ||
237 | * How many query messages have we received 'recently' that | ||
238 | * have not yet been claimed as cover traffic? | ||
239 | */ | ||
240 | extern unsigned int GSF_cover_query_count; | ||
241 | |||
242 | /** | ||
243 | * How many content messages have we received 'recently' that | ||
244 | * have not yet been claimed as cover traffic? | ||
245 | */ | ||
246 | extern unsigned int GSF_cover_content_count; | ||
247 | |||
248 | /** | ||
249 | * Our block context. | ||
250 | */ | ||
251 | extern struct GNUNET_BLOCK_Context *GSF_block_ctx; | ||
252 | |||
253 | /** | ||
254 | * Are we introducing randomized delays for better anonymity? | ||
255 | */ | ||
256 | extern int GSF_enable_randomized_delays; | ||
257 | |||
258 | /** | ||
259 | * Size of the datastore queue we assume for common requests. | ||
260 | */ | ||
261 | extern unsigned int GSF_datastore_queue_size; | ||
262 | |||
263 | |||
264 | /** | ||
265 | * Function to be called after we're done processing | ||
266 | * replies from the local lookup. If the result status | ||
267 | * code indicates that there may be more replies, plan | ||
268 | * forwarding the request. | ||
269 | * | ||
270 | * @param cls closure (NULL) | ||
271 | * @param pr the pending request we were processing | ||
272 | * @param result final datastore lookup result | ||
273 | */ | ||
274 | void | ||
275 | GSF_consider_forwarding (void *cls, | ||
276 | struct GSF_PendingRequest *pr, | ||
277 | enum GNUNET_BLOCK_ReplyEvaluationResult result); | ||
278 | |||
279 | |||
280 | /** | ||
281 | * Test if the DATABASE (GET) load on this peer is too high | ||
282 | * to even consider processing the query at | ||
283 | * all. | ||
284 | * | ||
285 | * @return #GNUNET_YES if the load is too high to do anything (load high) | ||
286 | * #GNUNET_NO to process normally (load normal) | ||
287 | * #GNUNET_SYSERR to process for free (load low) | ||
288 | */ | ||
289 | int | ||
290 | GSF_test_get_load_too_high_ (uint32_t priority); | ||
291 | |||
292 | |||
293 | /** | ||
294 | * We've just now completed a datastore request. Update our | ||
295 | * datastore load calculations. | ||
296 | * | ||
297 | * @param start time when the datastore request was issued | ||
298 | */ | ||
299 | void | ||
300 | GSF_update_datastore_delay_ (struct GNUNET_TIME_Absolute start); | ||
301 | |||
302 | |||
303 | #endif | ||
304 | /* 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 | */ | ||
32 | struct 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 | */ | ||
44 | typedef 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 | */ | ||
62 | struct GSF_CadetRequest * | ||
63 | GSF_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 | */ | ||
77 | int | ||
78 | GSF_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 | */ | ||
89 | void | ||
90 | GSF_cadet_query_cancel (struct GSF_CadetRequest *sr); | ||
91 | |||
92 | |||
93 | /** | ||
94 | * Initialize subsystem for non-anonymous file-sharing. | ||
95 | */ | ||
96 | void | ||
97 | GSF_cadet_start_server (void); | ||
98 | |||
99 | |||
100 | /** | ||
101 | * Shutdown subsystem for non-anonymous file-sharing. | ||
102 | */ | ||
103 | void | ||
104 | GSF_cadet_stop_server (void); | ||
105 | |||
106 | /** | ||
107 | * Cadet channel for creating outbound channels. | ||
108 | */ | ||
109 | extern struct GNUNET_CADET_Handle *cadet_handle; | ||
110 | |||
111 | /** | ||
112 | * Map from peer identities to 'struct CadetHandles' with cadet | ||
113 | * channels to those peers. | ||
114 | */ | ||
115 | extern struct GNUNET_CONTAINER_MultiPeerMap *cadet_map; | ||
116 | |||
117 | |||
118 | GNUNET_NETWORK_STRUCT_BEGIN | ||
119 | |||
120 | /** | ||
121 | * Query from one peer, asking the other for CHK-data. | ||
122 | */ | ||
123 | struct 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 | */ | ||
145 | struct 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 | |||
165 | GNUNET_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 | */ | ||
51 | struct CadetHandle; | ||
52 | |||
53 | |||
54 | /** | ||
55 | * Handle for a request that is going out via cadet API. | ||
56 | */ | ||
57 | struct 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 | */ | ||
105 | struct 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 | */ | ||
152 | struct GNUNET_CADET_Handle *cadet_handle; | ||
153 | |||
154 | /** | ||
155 | * Map from peer identities to 'struct CadetHandles' with cadet | ||
156 | * channels to those peers. | ||
157 | */ | ||
158 | struct 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 | */ | ||
169 | static void | ||
170 | transmit_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 | */ | ||
182 | static int | ||
183 | move_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 | */ | ||
205 | static int | ||
206 | check_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 | */ | ||
218 | static void | ||
219 | reset_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 | */ | ||
228 | static void | ||
229 | reset_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 | */ | ||
240 | struct 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 | */ | ||
278 | static int | ||
279 | process_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 | */ | ||
306 | static int | ||
307 | free_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 | */ | ||
323 | static void | ||
324 | handle_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 | */ | ||
383 | static void | ||
384 | disconnect_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 | */ | ||
429 | static void | ||
430 | window_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 | */ | ||
449 | static void | ||
450 | reset_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 | */ | ||
490 | static void | ||
491 | cadet_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 | */ | ||
512 | static void | ||
513 | reset_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 | */ | ||
527 | static void | ||
528 | transmit_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 | */ | ||
566 | static struct CadetHandle * | ||
567 | get_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 | */ | ||
629 | struct GSF_CadetRequest * | ||
630 | GSF_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 | */ | ||
662 | void | ||
663 | GSF_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 | */ | ||
702 | int | ||
703 | GSF_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 8bfe91cf0..000000000 --- a/src/fs/gnunet-service-fs_cadet_server.c +++ /dev/null | |||
@@ -1,545 +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 | */ | ||
49 | struct 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 | */ | ||
71 | struct 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 | */ | ||
123 | static struct GNUNET_CADET_Port *cadet_port; | ||
124 | |||
125 | /** | ||
126 | * Head of DLL of cadet clients. | ||
127 | */ | ||
128 | static struct CadetClient *sc_head; | ||
129 | |||
130 | /** | ||
131 | * Tail of DLL of cadet clients. | ||
132 | */ | ||
133 | static struct CadetClient *sc_tail; | ||
134 | |||
135 | /** | ||
136 | * Number of active cadet clients in the 'sc_*'-DLL. | ||
137 | */ | ||
138 | static unsigned int sc_count; | ||
139 | |||
140 | /** | ||
141 | * Maximum allowed number of cadet clients. | ||
142 | */ | ||
143 | static 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 | */ | ||
151 | static void | ||
152 | timeout_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 | */ | ||
172 | static void | ||
173 | refresh_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 | */ | ||
189 | static void | ||
190 | continue_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 | */ | ||
225 | static void | ||
226 | handle_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 | */ | ||
333 | static void | ||
334 | handle_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 | */ | ||
375 | static void * | ||
376 | connect_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 | */ | ||
419 | static void | ||
420 | disconnect_cb (void *cls, | ||
421 | const struct GNUNET_CADET_Channel *channel) | ||
422 | { | ||
423 | struct CadetClient *sc = cls; | ||
424 | struct WriteQueueItem *wqi; | ||
425 | |||
426 | if (NULL == sc) | ||
427 | return; | ||
428 | sc->channel = NULL; | ||
429 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
430 | "Terminating cadet connection with client %p\n", | ||
431 | sc); | ||
432 | GNUNET_STATISTICS_update (GSF_stats, | ||
433 | gettext_noop ("# cadet connections active"), -1, | ||
434 | GNUNET_NO); | ||
435 | if (NULL != sc->terminate_task) | ||
436 | GNUNET_SCHEDULER_cancel (sc->terminate_task); | ||
437 | if (NULL != sc->timeout_task) | ||
438 | GNUNET_SCHEDULER_cancel (sc->timeout_task); | ||
439 | if (NULL != sc->qe) | ||
440 | GNUNET_DATASTORE_cancel (sc->qe); | ||
441 | while (NULL != (wqi = sc->wqi_head)) | ||
442 | { | ||
443 | GNUNET_CONTAINER_DLL_remove (sc->wqi_head, | ||
444 | sc->wqi_tail, | ||
445 | wqi); | ||
446 | GNUNET_free (wqi); | ||
447 | } | ||
448 | GNUNET_CONTAINER_DLL_remove (sc_head, | ||
449 | sc_tail, | ||
450 | sc); | ||
451 | sc_count--; | ||
452 | GNUNET_free (sc); | ||
453 | } | ||
454 | |||
455 | |||
456 | /** | ||
457 | * Function called whenever an MQ-channel's transmission window size changes. | ||
458 | * | ||
459 | * The first callback in an outgoing channel will be with a non-zero value | ||
460 | * and will mean the channel is connected to the destination. | ||
461 | * | ||
462 | * For an incoming channel it will be called immediately after the | ||
463 | * #GNUNET_CADET_ConnectEventHandler, also with a non-zero value. | ||
464 | * | ||
465 | * @param cls Channel closure. | ||
466 | * @param channel Connection to the other end (henceforth invalid). | ||
467 | * @param window_size New window size. If the is more messages than buffer size | ||
468 | * this value will be negative.. | ||
469 | */ | ||
470 | static void | ||
471 | window_change_cb (void *cls, | ||
472 | const struct GNUNET_CADET_Channel *channel, | ||
473 | int window_size) | ||
474 | { | ||
475 | /* FIXME: could do flow control here... */ | ||
476 | } | ||
477 | |||
478 | |||
479 | /** | ||
480 | * Initialize subsystem for non-anonymous file-sharing. | ||
481 | */ | ||
482 | void | ||
483 | GSF_cadet_start_server () | ||
484 | { | ||
485 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
486 | GNUNET_MQ_hd_fixed_size (request, | ||
487 | GNUNET_MESSAGE_TYPE_FS_CADET_QUERY, | ||
488 | struct CadetQueryMessage, | ||
489 | NULL), | ||
490 | GNUNET_MQ_handler_end () | ||
491 | }; | ||
492 | struct GNUNET_HashCode port; | ||
493 | |||
494 | if (GNUNET_YES != | ||
495 | GNUNET_CONFIGURATION_get_value_number (GSF_cfg, | ||
496 | "fs", | ||
497 | "MAX_CADET_CLIENTS", | ||
498 | &sc_count_max)) | ||
499 | return; | ||
500 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
501 | "Initializing cadet FS server with a limit of %llu connections\n", | ||
502 | sc_count_max); | ||
503 | cadet_map = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_YES); | ||
504 | cadet_handle = GNUNET_CADET_connect (GSF_cfg); | ||
505 | GNUNET_assert (NULL != cadet_handle); | ||
506 | GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER, | ||
507 | strlen (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER), | ||
508 | &port); | ||
509 | cadet_port = GNUNET_CADET_open_port (cadet_handle, | ||
510 | &port, | ||
511 | &connect_cb, | ||
512 | NULL, | ||
513 | &window_change_cb, | ||
514 | &disconnect_cb, | ||
515 | handlers); | ||
516 | } | ||
517 | |||
518 | |||
519 | /** | ||
520 | * Shutdown subsystem for non-anonymous file-sharing. | ||
521 | */ | ||
522 | void | ||
523 | GSF_cadet_stop_server () | ||
524 | { | ||
525 | GNUNET_CONTAINER_multipeermap_iterate (cadet_map, | ||
526 | &GSF_cadet_release_clients, | ||
527 | NULL); | ||
528 | GNUNET_CONTAINER_multipeermap_destroy (cadet_map); | ||
529 | cadet_map = NULL; | ||
530 | if (NULL != cadet_port) | ||
531 | { | ||
532 | GNUNET_CADET_close_port (cadet_port); | ||
533 | cadet_port = NULL; | ||
534 | } | ||
535 | if (NULL != cadet_handle) | ||
536 | { | ||
537 | GNUNET_CADET_disconnect (cadet_handle); | ||
538 | cadet_handle = NULL; | ||
539 | } | ||
540 | GNUNET_assert (NULL == sc_head); | ||
541 | GNUNET_assert (0 == sc_count); | ||
542 | } | ||
543 | |||
544 | |||
545 | /* 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 74dd42daf..000000000 --- a/src/fs/gnunet-service-fs_cp.c +++ /dev/null | |||
@@ -1,1659 +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 | */ | ||
64 | struct 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 | * Priority of this request. | ||
98 | */ | ||
99 | uint32_t priority; | ||
100 | }; | ||
101 | |||
102 | |||
103 | /** | ||
104 | * Handle for an entry in our delay list. | ||
105 | */ | ||
106 | struct GSF_DelayedHandle | ||
107 | { | ||
108 | /** | ||
109 | * Kept in a doubly-linked list. | ||
110 | */ | ||
111 | struct GSF_DelayedHandle *next; | ||
112 | |||
113 | /** | ||
114 | * Kept in a doubly-linked list. | ||
115 | */ | ||
116 | struct GSF_DelayedHandle *prev; | ||
117 | |||
118 | /** | ||
119 | * Peer this transmission belongs to. | ||
120 | */ | ||
121 | struct GSF_ConnectedPeer *cp; | ||
122 | |||
123 | /** | ||
124 | * Envelope of the message that was delayed. | ||
125 | */ | ||
126 | struct GNUNET_MQ_Envelope *env; | ||
127 | |||
128 | /** | ||
129 | * Task for the delay. | ||
130 | */ | ||
131 | struct GNUNET_SCHEDULER_Task *delay_task; | ||
132 | |||
133 | /** | ||
134 | * Size of the message. | ||
135 | */ | ||
136 | size_t msize; | ||
137 | }; | ||
138 | |||
139 | |||
140 | /** | ||
141 | * Information per peer and request. | ||
142 | */ | ||
143 | struct PeerRequest | ||
144 | { | ||
145 | /** | ||
146 | * Handle to generic request (generic: from peer or local client). | ||
147 | */ | ||
148 | struct GSF_PendingRequest *pr; | ||
149 | |||
150 | /** | ||
151 | * Which specific peer issued this request? | ||
152 | */ | ||
153 | struct GSF_ConnectedPeer *cp; | ||
154 | |||
155 | /** | ||
156 | * Task for asynchronous stopping of this request. | ||
157 | */ | ||
158 | struct GNUNET_SCHEDULER_Task *kill_task; | ||
159 | }; | ||
160 | |||
161 | |||
162 | /** | ||
163 | * A connected peer. | ||
164 | */ | ||
165 | struct GSF_ConnectedPeer | ||
166 | { | ||
167 | /** | ||
168 | * Performance data for this peer. | ||
169 | */ | ||
170 | struct GSF_PeerPerformanceData ppd; | ||
171 | |||
172 | /** | ||
173 | * Time until when we blocked this peer from migrating | ||
174 | * data to us. | ||
175 | */ | ||
176 | struct GNUNET_TIME_Absolute last_migration_block; | ||
177 | |||
178 | /** | ||
179 | * Task scheduled to revive migration to this peer. | ||
180 | */ | ||
181 | struct GNUNET_SCHEDULER_Task *mig_revive_task; | ||
182 | |||
183 | /** | ||
184 | * Messages (replies, queries, content migration) we would like to | ||
185 | * send to this peer in the near future. Sorted by priority, head. | ||
186 | */ | ||
187 | struct GSF_PeerTransmitHandle *pth_head; | ||
188 | |||
189 | /** | ||
190 | * Messages (replies, queries, content migration) we would like to | ||
191 | * send to this peer in the near future. Sorted by priority, tail. | ||
192 | */ | ||
193 | struct GSF_PeerTransmitHandle *pth_tail; | ||
194 | |||
195 | /** | ||
196 | * Messages (replies, queries, content migration) we would like to | ||
197 | * send to this peer in the near future. Sorted by priority, head. | ||
198 | */ | ||
199 | struct GSF_DelayedHandle *delayed_head; | ||
200 | |||
201 | /** | ||
202 | * Messages (replies, queries, content migration) we would like to | ||
203 | * send to this peer in the near future. Sorted by priority, tail. | ||
204 | */ | ||
205 | struct GSF_DelayedHandle *delayed_tail; | ||
206 | |||
207 | /** | ||
208 | * Task scheduled if we need to retry bandwidth reservation later. | ||
209 | */ | ||
210 | struct GNUNET_SCHEDULER_Task *rc_delay_task; | ||
211 | |||
212 | /** | ||
213 | * Active requests from this neighbour, map of query to `struct PeerRequest`. | ||
214 | */ | ||
215 | struct GNUNET_CONTAINER_MultiHashMap *request_map; | ||
216 | |||
217 | /** | ||
218 | * Handle for an active request for transmission to this | ||
219 | * peer. | ||
220 | */ | ||
221 | struct GNUNET_MQ_Handle *mq; | ||
222 | |||
223 | /** | ||
224 | * Increase in traffic preference still to be submitted | ||
225 | * to the core service for this peer. | ||
226 | */ | ||
227 | uint64_t inc_preference; | ||
228 | |||
229 | /** | ||
230 | * Number of entries in @e delayed_head DLL. | ||
231 | */ | ||
232 | unsigned int delay_queue_size; | ||
233 | |||
234 | /** | ||
235 | * Respect rating for this peer on disk. | ||
236 | */ | ||
237 | uint32_t disk_respect; | ||
238 | |||
239 | /** | ||
240 | * Which offset in @e last_p2p_replies will be updated next? | ||
241 | * (we go round-robin). | ||
242 | */ | ||
243 | unsigned int last_p2p_replies_woff; | ||
244 | |||
245 | /** | ||
246 | * Which offset in @e last_client_replies will be updated next? | ||
247 | * (we go round-robin). | ||
248 | */ | ||
249 | unsigned int last_client_replies_woff; | ||
250 | |||
251 | /** | ||
252 | * Current offset into @e last_request_times ring buffer. | ||
253 | */ | ||
254 | unsigned int last_request_times_off; | ||
255 | |||
256 | /** | ||
257 | * Handle to the PEERSTORE iterate request for peer respect value | ||
258 | */ | ||
259 | struct GNUNET_PEERSTORE_IterateContext *respect_iterate_req; | ||
260 | }; | ||
261 | |||
262 | |||
263 | /** | ||
264 | * Map from peer identities to `struct GSF_ConnectPeer` entries. | ||
265 | */ | ||
266 | static struct GNUNET_CONTAINER_MultiPeerMap *cp_map; | ||
267 | |||
268 | /** | ||
269 | * Handle to peerstore service. | ||
270 | */ | ||
271 | static struct GNUNET_PEERSTORE_Handle *peerstore; | ||
272 | |||
273 | /** | ||
274 | * Task used to flush respect values to disk. | ||
275 | */ | ||
276 | static struct GNUNET_SCHEDULER_Task *fr_task; | ||
277 | |||
278 | |||
279 | /** | ||
280 | * Update the latency information kept for the given peer. | ||
281 | * | ||
282 | * @param id peer record to update | ||
283 | * @param latency current latency value | ||
284 | */ | ||
285 | void | ||
286 | GSF_update_peer_latency_ (const struct GNUNET_PeerIdentity *id, | ||
287 | struct GNUNET_TIME_Relative latency) | ||
288 | { | ||
289 | struct GSF_ConnectedPeer *cp; | ||
290 | |||
291 | cp = GSF_peer_get_ (id); | ||
292 | if (NULL == cp) | ||
293 | return; /* we're not yet connected at the core level, ignore */ | ||
294 | GNUNET_LOAD_value_set_decline (cp->ppd.transmission_delay, | ||
295 | latency); | ||
296 | } | ||
297 | |||
298 | |||
299 | /** | ||
300 | * Return the performance data record for the given peer | ||
301 | * | ||
302 | * @param cp peer to query | ||
303 | * @return performance data record for the peer | ||
304 | */ | ||
305 | struct GSF_PeerPerformanceData * | ||
306 | GSF_get_peer_performance_data_ (struct GSF_ConnectedPeer *cp) | ||
307 | { | ||
308 | return &cp->ppd; | ||
309 | } | ||
310 | |||
311 | |||
312 | /** | ||
313 | * Core is ready to transmit to a peer, get the message. | ||
314 | * | ||
315 | * @param cp which peer to send a message to | ||
316 | */ | ||
317 | static void | ||
318 | peer_transmit (struct GSF_ConnectedPeer *cp); | ||
319 | |||
320 | |||
321 | /** | ||
322 | * If ready (bandwidth reserved), try to schedule transmission via | ||
323 | * core for the given handle. | ||
324 | * | ||
325 | * @param pth transmission handle to schedule | ||
326 | */ | ||
327 | static void | ||
328 | schedule_transmission (struct GSF_PeerTransmitHandle *pth) | ||
329 | { | ||
330 | struct GSF_ConnectedPeer *cp; | ||
331 | struct GNUNET_PeerIdentity target; | ||
332 | |||
333 | cp = pth->cp; | ||
334 | GNUNET_assert (0 != cp->ppd.pid); | ||
335 | GNUNET_PEER_resolve (cp->ppd.pid, &target); | ||
336 | |||
337 | peer_transmit (cp); | ||
338 | } | ||
339 | |||
340 | |||
341 | /** | ||
342 | * Core is ready to transmit to a peer, get the message. | ||
343 | * | ||
344 | * @param cp which peer to send a message to | ||
345 | */ | ||
346 | static void | ||
347 | peer_transmit (struct GSF_ConnectedPeer *cp) | ||
348 | { | ||
349 | struct GSF_PeerTransmitHandle *pth = cp->pth_head; | ||
350 | struct GSF_PeerTransmitHandle *pos; | ||
351 | |||
352 | if (NULL == pth) | ||
353 | return; | ||
354 | GNUNET_CONTAINER_DLL_remove (cp->pth_head, | ||
355 | cp->pth_tail, | ||
356 | pth); | ||
357 | if (GNUNET_YES == pth->is_query) | ||
358 | { | ||
359 | cp->ppd.last_request_times[(cp->last_request_times_off++) | ||
360 | % MAX_QUEUE_PER_PEER] = | ||
361 | GNUNET_TIME_absolute_get (); | ||
362 | GNUNET_assert (0 < cp->ppd.pending_queries--); | ||
363 | } | ||
364 | else if (GNUNET_NO == pth->is_query) | ||
365 | { | ||
366 | GNUNET_assert (0 < cp->ppd.pending_replies--); | ||
367 | } | ||
368 | GNUNET_LOAD_update (cp->ppd.transmission_delay, | ||
369 | GNUNET_TIME_absolute_get_duration | ||
370 | (pth->transmission_request_start_time).rel_value_us); | ||
371 | GNUNET_MQ_send (cp->mq, | ||
372 | pth->env); | ||
373 | GNUNET_free (pth); | ||
374 | if (NULL != (pos = cp->pth_head)) | ||
375 | { | ||
376 | GNUNET_assert (pos != pth); | ||
377 | schedule_transmission (pos); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | |||
382 | /** | ||
383 | * Function called by PEERSTORE with peer respect record | ||
384 | * | ||
385 | * @param cls handle to connected peer entry | ||
386 | * @param record peerstore record information | ||
387 | * @param emsg error message, or NULL if no errors | ||
388 | */ | ||
389 | static void | ||
390 | peer_respect_cb (void *cls, | ||
391 | const struct GNUNET_PEERSTORE_Record *record, | ||
392 | const char *emsg) | ||
393 | { | ||
394 | struct GSF_ConnectedPeer *cp = cls; | ||
395 | |||
396 | GNUNET_assert (NULL != cp->respect_iterate_req); | ||
397 | if ((NULL != record) && | ||
398 | (sizeof(cp->disk_respect) == record->value_size)) | ||
399 | { | ||
400 | cp->disk_respect = *((uint32_t *) record->value); | ||
401 | cp->ppd.respect += *((uint32_t *) record->value); | ||
402 | } | ||
403 | GSF_push_start_ (cp); | ||
404 | if (NULL != record) | ||
405 | GNUNET_PEERSTORE_iterate_cancel (cp->respect_iterate_req); | ||
406 | cp->respect_iterate_req = NULL; | ||
407 | } | ||
408 | |||
409 | |||
410 | /** | ||
411 | * Function called for each pending request whenever a new | ||
412 | * peer connects, giving us a chance to decide about submitting | ||
413 | * the existing request to the new peer. | ||
414 | * | ||
415 | * @param cls the `struct GSF_ConnectedPeer` of the new peer | ||
416 | * @param key query for the request | ||
417 | * @param pr handle to the pending request | ||
418 | * @return #GNUNET_YES to continue to iterate | ||
419 | */ | ||
420 | static int | ||
421 | consider_peer_for_forwarding (void *cls, | ||
422 | const struct GNUNET_HashCode *key, | ||
423 | struct GSF_PendingRequest *pr) | ||
424 | { | ||
425 | struct GSF_ConnectedPeer *cp = cls; | ||
426 | struct GNUNET_PeerIdentity pid; | ||
427 | |||
428 | if (GNUNET_YES != | ||
429 | GSF_pending_request_test_active_ (pr)) | ||
430 | return GNUNET_YES; /* request is not actually active, skip! */ | ||
431 | GSF_connected_peer_get_identity_ (cp, &pid); | ||
432 | if (GNUNET_YES != | ||
433 | GSF_pending_request_test_target_ (pr, &pid)) | ||
434 | { | ||
435 | GNUNET_STATISTICS_update (GSF_stats, | ||
436 | gettext_noop ("# Loopback routes suppressed"), | ||
437 | 1, | ||
438 | GNUNET_NO); | ||
439 | return GNUNET_YES; | ||
440 | } | ||
441 | GSF_plan_add_ (cp, pr); | ||
442 | return GNUNET_YES; | ||
443 | } | ||
444 | |||
445 | |||
446 | void * | ||
447 | GSF_peer_connect_handler (void *cls, | ||
448 | const struct GNUNET_PeerIdentity *peer, | ||
449 | struct GNUNET_MQ_Handle *mq) | ||
450 | { | ||
451 | struct GSF_ConnectedPeer *cp; | ||
452 | |||
453 | if (0 == | ||
454 | GNUNET_memcmp (&GSF_my_id, | ||
455 | peer)) | ||
456 | return NULL; | ||
457 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
458 | "Connected to peer %s\n", | ||
459 | GNUNET_i2s (peer)); | ||
460 | cp = GNUNET_new (struct GSF_ConnectedPeer); | ||
461 | cp->ppd.pid = GNUNET_PEER_intern (peer); | ||
462 | cp->ppd.peer = peer; | ||
463 | cp->mq = mq; | ||
464 | cp->ppd.transmission_delay = GNUNET_LOAD_value_init (GNUNET_TIME_UNIT_ZERO); | ||
465 | |||
466 | cp->request_map = GNUNET_CONTAINER_multihashmap_create (128, | ||
467 | GNUNET_YES); | ||
468 | GNUNET_break (GNUNET_OK == | ||
469 | GNUNET_CONTAINER_multipeermap_put (cp_map, | ||
470 | GSF_connected_peer_get_identity2_ ( | ||
471 | cp), | ||
472 | cp, | ||
473 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
474 | GNUNET_STATISTICS_set (GSF_stats, | ||
475 | gettext_noop ("# peers connected"), | ||
476 | GNUNET_CONTAINER_multipeermap_size (cp_map), | ||
477 | GNUNET_NO); | ||
478 | cp->respect_iterate_req | ||
479 | = GNUNET_PEERSTORE_iterate (peerstore, | ||
480 | "fs", | ||
481 | peer, | ||
482 | "respect", | ||
483 | &peer_respect_cb, | ||
484 | cp); | ||
485 | GSF_iterate_pending_requests_ (&consider_peer_for_forwarding, | ||
486 | cp); | ||
487 | return cp; | ||
488 | } | ||
489 | |||
490 | |||
491 | /** | ||
492 | * It may be time to re-start migrating content to this | ||
493 | * peer. Check, and if so, restart migration. | ||
494 | * | ||
495 | * @param cls the `struct GSF_ConnectedPeer` | ||
496 | */ | ||
497 | static void | ||
498 | revive_migration (void *cls) | ||
499 | { | ||
500 | struct GSF_ConnectedPeer *cp = cls; | ||
501 | struct GNUNET_TIME_Relative bt; | ||
502 | |||
503 | cp->mig_revive_task = NULL; | ||
504 | bt = GNUNET_TIME_absolute_get_remaining (cp->ppd.migration_blocked_until); | ||
505 | if (0 != bt.rel_value_us) | ||
506 | { | ||
507 | /* still time left... */ | ||
508 | cp->mig_revive_task = | ||
509 | GNUNET_SCHEDULER_add_delayed (bt, &revive_migration, cp); | ||
510 | return; | ||
511 | } | ||
512 | GSF_push_start_ (cp); | ||
513 | } | ||
514 | |||
515 | |||
516 | struct GSF_ConnectedPeer * | ||
517 | GSF_peer_get_ (const struct GNUNET_PeerIdentity *peer) | ||
518 | { | ||
519 | if (NULL == cp_map) | ||
520 | return NULL; | ||
521 | return GNUNET_CONTAINER_multipeermap_get (cp_map, peer); | ||
522 | } | ||
523 | |||
524 | |||
525 | /** | ||
526 | * Handle P2P #GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP message. | ||
527 | * | ||
528 | * @param cls closure, the `struct GSF_ConnectedPeer` | ||
529 | * @param msm the actual message | ||
530 | */ | ||
531 | void | ||
532 | handle_p2p_migration_stop (void *cls, | ||
533 | const struct MigrationStopMessage *msm) | ||
534 | { | ||
535 | struct GSF_ConnectedPeer *cp = cls; | ||
536 | struct GNUNET_TIME_Relative bt; | ||
537 | |||
538 | GNUNET_STATISTICS_update (GSF_stats, | ||
539 | gettext_noop ("# migration stop messages received"), | ||
540 | 1, GNUNET_NO); | ||
541 | bt = GNUNET_TIME_relative_ntoh (msm->duration); | ||
542 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
543 | _ ("Migration of content to peer `%s' blocked for %s\n"), | ||
544 | GNUNET_i2s (cp->ppd.peer), | ||
545 | GNUNET_STRINGS_relative_time_to_string (bt, GNUNET_YES)); | ||
546 | cp->ppd.migration_blocked_until = GNUNET_TIME_relative_to_absolute (bt); | ||
547 | if ((NULL == cp->mig_revive_task) && | ||
548 | (NULL == cp->respect_iterate_req)) | ||
549 | { | ||
550 | GSF_push_stop_ (cp); | ||
551 | cp->mig_revive_task = | ||
552 | GNUNET_SCHEDULER_add_delayed (bt, | ||
553 | &revive_migration, cp); | ||
554 | } | ||
555 | } | ||
556 | |||
557 | |||
558 | /** | ||
559 | * Free resources associated with the given peer request. | ||
560 | * | ||
561 | * @param peerreq request to free | ||
562 | */ | ||
563 | static void | ||
564 | free_pending_request (struct PeerRequest *peerreq) | ||
565 | { | ||
566 | struct GSF_ConnectedPeer *cp = peerreq->cp; | ||
567 | struct GSF_PendingRequestData *prd; | ||
568 | |||
569 | prd = GSF_pending_request_get_data_ (peerreq->pr); | ||
570 | if (NULL != peerreq->kill_task) | ||
571 | { | ||
572 | GNUNET_SCHEDULER_cancel (peerreq->kill_task); | ||
573 | peerreq->kill_task = NULL; | ||
574 | } | ||
575 | GNUNET_STATISTICS_update (GSF_stats, | ||
576 | gettext_noop ("# P2P searches active"), | ||
577 | -1, | ||
578 | GNUNET_NO); | ||
579 | GNUNET_break (GNUNET_YES == | ||
580 | GNUNET_CONTAINER_multihashmap_remove (cp->request_map, | ||
581 | &prd->query, | ||
582 | peerreq)); | ||
583 | GNUNET_free (peerreq); | ||
584 | } | ||
585 | |||
586 | |||
587 | /** | ||
588 | * Cancel all requests associated with the peer. | ||
589 | * | ||
590 | * @param cls unused | ||
591 | * @param query hash code of the request | ||
592 | * @param value the `struct GSF_PendingRequest` | ||
593 | * @return #GNUNET_YES (continue to iterate) | ||
594 | */ | ||
595 | static int | ||
596 | cancel_pending_request (void *cls, | ||
597 | const struct GNUNET_HashCode *query, | ||
598 | void *value) | ||
599 | { | ||
600 | struct PeerRequest *peerreq = value; | ||
601 | struct GSF_PendingRequest *pr = peerreq->pr; | ||
602 | |||
603 | free_pending_request (peerreq); | ||
604 | GSF_pending_request_cancel_ (pr, | ||
605 | GNUNET_NO); | ||
606 | return GNUNET_OK; | ||
607 | } | ||
608 | |||
609 | |||
610 | /** | ||
611 | * Free the given request. | ||
612 | * | ||
613 | * @param cls the request to free | ||
614 | */ | ||
615 | static void | ||
616 | peer_request_destroy (void *cls) | ||
617 | { | ||
618 | struct PeerRequest *peerreq = cls; | ||
619 | struct GSF_PendingRequest *pr = peerreq->pr; | ||
620 | struct GSF_PendingRequestData *prd; | ||
621 | |||
622 | peerreq->kill_task = NULL; | ||
623 | prd = GSF_pending_request_get_data_ (pr); | ||
624 | cancel_pending_request (NULL, | ||
625 | &prd->query, | ||
626 | peerreq); | ||
627 | } | ||
628 | |||
629 | |||
630 | /** | ||
631 | * The artificial delay is over, transmit the message now. | ||
632 | * | ||
633 | * @param cls the `struct GSF_DelayedHandle` with the message | ||
634 | */ | ||
635 | static void | ||
636 | transmit_delayed_now (void *cls) | ||
637 | { | ||
638 | struct GSF_DelayedHandle *dh = cls; | ||
639 | struct GSF_ConnectedPeer *cp = dh->cp; | ||
640 | |||
641 | GNUNET_CONTAINER_DLL_remove (cp->delayed_head, | ||
642 | cp->delayed_tail, | ||
643 | dh); | ||
644 | cp->delay_queue_size--; | ||
645 | GSF_peer_transmit_ (cp, | ||
646 | GNUNET_NO, | ||
647 | UINT32_MAX, | ||
648 | dh->env); | ||
649 | GNUNET_free (dh); | ||
650 | } | ||
651 | |||
652 | |||
653 | /** | ||
654 | * Get the randomized delay a response should be subjected to. | ||
655 | * | ||
656 | * @return desired delay | ||
657 | */ | ||
658 | static struct GNUNET_TIME_Relative | ||
659 | get_randomized_delay () | ||
660 | { | ||
661 | struct GNUNET_TIME_Relative ret; | ||
662 | |||
663 | ret = | ||
664 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, | ||
665 | GNUNET_CRYPTO_random_u32 | ||
666 | (GNUNET_CRYPTO_QUALITY_WEAK, | ||
667 | 2 * GSF_avg_latency.rel_value_us + 1)); | ||
668 | #if INSANE_STATISTICS | ||
669 | GNUNET_STATISTICS_update (GSF_stats, | ||
670 | gettext_noop | ||
671 | ("# artificial delays introduced (ms)"), | ||
672 | ret.rel_value_us / 1000LL, GNUNET_NO); | ||
673 | #endif | ||
674 | return ret; | ||
675 | } | ||
676 | |||
677 | |||
678 | /** | ||
679 | * Handle a reply to a pending request. Also called if a request | ||
680 | * expires (then with data == NULL). The handler may be called | ||
681 | * many times (depending on the request type), but will not be | ||
682 | * called during or after a call to GSF_pending_request_cancel | ||
683 | * and will also not be called anymore after a call signalling | ||
684 | * expiration. | ||
685 | * | ||
686 | * @param cls `struct PeerRequest` this is an answer for | ||
687 | * @param eval evaluation of the result | ||
688 | * @param pr handle to the original pending request | ||
689 | * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown" | ||
690 | * @param expiration when does @a data expire? | ||
691 | * @param last_transmission when did we last transmit a request for this block | ||
692 | * @param type type of the block | ||
693 | * @param data response data, NULL on request expiration | ||
694 | * @param data_len number of bytes in @a data | ||
695 | */ | ||
696 | static void | ||
697 | handle_p2p_reply (void *cls, | ||
698 | enum GNUNET_BLOCK_ReplyEvaluationResult eval, | ||
699 | struct GSF_PendingRequest *pr, | ||
700 | uint32_t reply_anonymity_level, | ||
701 | struct GNUNET_TIME_Absolute expiration, | ||
702 | struct GNUNET_TIME_Absolute last_transmission, | ||
703 | enum GNUNET_BLOCK_Type type, | ||
704 | const void *data, | ||
705 | size_t data_len) | ||
706 | { | ||
707 | struct PeerRequest *peerreq = cls; | ||
708 | struct GSF_ConnectedPeer *cp = peerreq->cp; | ||
709 | struct GSF_PendingRequestData *prd; | ||
710 | struct GNUNET_MQ_Envelope *env; | ||
711 | struct PutMessage *pm; | ||
712 | size_t msize; | ||
713 | |||
714 | GNUNET_assert (data_len + sizeof(struct PutMessage) < | ||
715 | GNUNET_MAX_MESSAGE_SIZE); | ||
716 | GNUNET_assert (peerreq->pr == pr); | ||
717 | prd = GSF_pending_request_get_data_ (pr); | ||
718 | if (NULL == data) | ||
719 | { | ||
720 | free_pending_request (peerreq); | ||
721 | return; | ||
722 | } | ||
723 | GNUNET_break (GNUNET_BLOCK_TYPE_ANY != type); | ||
724 | if ( (prd->type != type) && | ||
725 | (GNUNET_BLOCK_TYPE_ANY != prd->type) ) | ||
726 | { | ||
727 | GNUNET_STATISTICS_update (GSF_stats, | ||
728 | "# replies dropped due to type mismatch", | ||
729 | 1, GNUNET_NO); | ||
730 | return; | ||
731 | } | ||
732 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
733 | "Transmitting result for query `%s' to peer\n", | ||
734 | GNUNET_h2s (&prd->query)); | ||
735 | GNUNET_STATISTICS_update (GSF_stats, | ||
736 | "# replies received for other peers", | ||
737 | 1, | ||
738 | GNUNET_NO); | ||
739 | msize = sizeof(struct PutMessage) + data_len; | ||
740 | if (msize >= GNUNET_MAX_MESSAGE_SIZE) | ||
741 | { | ||
742 | GNUNET_break (0); | ||
743 | return; | ||
744 | } | ||
745 | if ( (UINT32_MAX != reply_anonymity_level) && | ||
746 | (reply_anonymity_level > 1) ) | ||
747 | { | ||
748 | if (reply_anonymity_level - 1 > GSF_cover_content_count) | ||
749 | { | ||
750 | GNUNET_STATISTICS_update (GSF_stats, | ||
751 | "# replies dropped due to insufficient cover traffic", | ||
752 | 1, GNUNET_NO); | ||
753 | return; | ||
754 | } | ||
755 | GSF_cover_content_count -= (reply_anonymity_level - 1); | ||
756 | } | ||
757 | |||
758 | env = GNUNET_MQ_msg_extra (pm, | ||
759 | data_len, | ||
760 | GNUNET_MESSAGE_TYPE_FS_PUT); | ||
761 | pm->type = htonl (type); | ||
762 | pm->expiration = GNUNET_TIME_absolute_hton (expiration); | ||
763 | GNUNET_memcpy (&pm[1], | ||
764 | data, | ||
765 | data_len); | ||
766 | if ((UINT32_MAX != reply_anonymity_level) && | ||
767 | (0 != reply_anonymity_level) && | ||
768 | (GNUNET_YES == GSF_enable_randomized_delays)) | ||
769 | { | ||
770 | struct GSF_DelayedHandle *dh; | ||
771 | |||
772 | dh = GNUNET_new (struct GSF_DelayedHandle); | ||
773 | dh->cp = cp; | ||
774 | dh->env = env; | ||
775 | dh->msize = msize; | ||
776 | GNUNET_CONTAINER_DLL_insert (cp->delayed_head, | ||
777 | cp->delayed_tail, | ||
778 | dh); | ||
779 | cp->delay_queue_size++; | ||
780 | dh->delay_task = | ||
781 | GNUNET_SCHEDULER_add_delayed (get_randomized_delay (), | ||
782 | &transmit_delayed_now, | ||
783 | dh); | ||
784 | } | ||
785 | else | ||
786 | { | ||
787 | GSF_peer_transmit_ (cp, | ||
788 | GNUNET_NO, | ||
789 | UINT32_MAX, | ||
790 | env); | ||
791 | } | ||
792 | if (GNUNET_BLOCK_REPLY_OK_LAST != eval) | ||
793 | return; | ||
794 | if (NULL == peerreq->kill_task) | ||
795 | { | ||
796 | GNUNET_STATISTICS_update (GSF_stats, | ||
797 | "# P2P searches destroyed due to ultimate reply", | ||
798 | 1, | ||
799 | GNUNET_NO); | ||
800 | peerreq->kill_task = | ||
801 | GNUNET_SCHEDULER_add_now (&peer_request_destroy, | ||
802 | peerreq); | ||
803 | } | ||
804 | } | ||
805 | |||
806 | |||
807 | /** | ||
808 | * Increase the peer's respect by a value. | ||
809 | * | ||
810 | * @param cp which peer to change the respect value on | ||
811 | * @param value is the int value by which the | ||
812 | * peer's credit is to be increased or decreased | ||
813 | * @returns the actual change in respect (positive or negative) | ||
814 | */ | ||
815 | static int | ||
816 | change_peer_respect (struct GSF_ConnectedPeer *cp, int value) | ||
817 | { | ||
818 | if (0 == value) | ||
819 | return 0; | ||
820 | GNUNET_assert (NULL != cp); | ||
821 | if (value > 0) | ||
822 | { | ||
823 | if (cp->ppd.respect + value < cp->ppd.respect) | ||
824 | { | ||
825 | value = UINT32_MAX - cp->ppd.respect; | ||
826 | cp->ppd.respect = UINT32_MAX; | ||
827 | } | ||
828 | else | ||
829 | cp->ppd.respect += value; | ||
830 | } | ||
831 | else | ||
832 | { | ||
833 | if (cp->ppd.respect < -value) | ||
834 | { | ||
835 | value = -cp->ppd.respect; | ||
836 | cp->ppd.respect = 0; | ||
837 | } | ||
838 | else | ||
839 | cp->ppd.respect += value; | ||
840 | } | ||
841 | return value; | ||
842 | } | ||
843 | |||
844 | |||
845 | /** | ||
846 | * We've received a request with the specified priority. Bound it | ||
847 | * according to how much we respect the given peer. | ||
848 | * | ||
849 | * @param prio_in requested priority | ||
850 | * @param cp the peer making the request | ||
851 | * @return effective priority | ||
852 | */ | ||
853 | static int32_t | ||
854 | bound_priority (uint32_t prio_in, | ||
855 | struct GSF_ConnectedPeer *cp) | ||
856 | { | ||
857 | #define N ((double) 128.0) | ||
858 | uint32_t ret; | ||
859 | double rret; | ||
860 | int ld; | ||
861 | |||
862 | ld = GSF_test_get_load_too_high_ (0); | ||
863 | if (GNUNET_SYSERR == ld) | ||
864 | { | ||
865 | #if INSANE_STATISTICS | ||
866 | GNUNET_STATISTICS_update (GSF_stats, | ||
867 | gettext_noop | ||
868 | ("# requests done for free (low load)"), 1, | ||
869 | GNUNET_NO); | ||
870 | #endif | ||
871 | return 0; /* excess resources */ | ||
872 | } | ||
873 | if (prio_in > INT32_MAX) | ||
874 | prio_in = INT32_MAX; | ||
875 | ret = -change_peer_respect (cp, -(int) prio_in); | ||
876 | if (ret > 0) | ||
877 | { | ||
878 | if (ret > GSF_current_priorities + N) | ||
879 | rret = GSF_current_priorities + N; | ||
880 | else | ||
881 | rret = ret; | ||
882 | GSF_current_priorities = (GSF_current_priorities * (N - 1) + rret) / N; | ||
883 | } | ||
884 | if ((GNUNET_YES == ld) && (ret > 0)) | ||
885 | { | ||
886 | /* try with charging */ | ||
887 | ld = GSF_test_get_load_too_high_ (ret); | ||
888 | } | ||
889 | if (GNUNET_YES == ld) | ||
890 | { | ||
891 | GNUNET_STATISTICS_update (GSF_stats, | ||
892 | gettext_noop | ||
893 | ("# request dropped, priority insufficient"), 1, | ||
894 | GNUNET_NO); | ||
895 | /* undo charge */ | ||
896 | change_peer_respect (cp, (int) ret); | ||
897 | return -1; /* not enough resources */ | ||
898 | } | ||
899 | else | ||
900 | { | ||
901 | GNUNET_STATISTICS_update (GSF_stats, | ||
902 | gettext_noop | ||
903 | ("# requests done for a price (normal load)"), | ||
904 | 1, | ||
905 | GNUNET_NO); | ||
906 | } | ||
907 | #undef N | ||
908 | return ret; | ||
909 | } | ||
910 | |||
911 | |||
912 | /** | ||
913 | * The priority level imposes a bound on the maximum | ||
914 | * value for the ttl that can be requested. | ||
915 | * | ||
916 | * @param ttl_in requested ttl | ||
917 | * @param prio given priority | ||
918 | * @return @a ttl_in if @a ttl_in is below the limit, | ||
919 | * otherwise the ttl-limit for the given @a prio | ||
920 | */ | ||
921 | static int32_t | ||
922 | bound_ttl (int32_t ttl_in, | ||
923 | uint32_t prio) | ||
924 | { | ||
925 | unsigned long long allowed; | ||
926 | |||
927 | if (ttl_in <= 0) | ||
928 | return ttl_in; | ||
929 | allowed = ((unsigned long long) prio) * TTL_DECREMENT / 1000; | ||
930 | if (ttl_in > allowed) | ||
931 | { | ||
932 | if (allowed >= (1 << 30)) | ||
933 | return 1 << 30; | ||
934 | return allowed; | ||
935 | } | ||
936 | return ttl_in; | ||
937 | } | ||
938 | |||
939 | |||
940 | /** | ||
941 | * Closure for #test_exist_cb(). | ||
942 | */ | ||
943 | struct TestExistClosure | ||
944 | { | ||
945 | /** | ||
946 | * Priority of the incoming request. | ||
947 | */ | ||
948 | int32_t priority; | ||
949 | |||
950 | /** | ||
951 | * Relative TTL of the incoming request. | ||
952 | */ | ||
953 | int32_t ttl; | ||
954 | |||
955 | /** | ||
956 | * Type of the incoming request. | ||
957 | */ | ||
958 | enum GNUNET_BLOCK_Type type; | ||
959 | |||
960 | /** | ||
961 | * Set to #GNUNET_YES if we are done handling the query. | ||
962 | */ | ||
963 | int finished; | ||
964 | }; | ||
965 | |||
966 | |||
967 | /** | ||
968 | * Test if the query already exists. If so, merge it, otherwise | ||
969 | * keep `finished` at #GNUNET_NO. | ||
970 | * | ||
971 | * @param cls our `struct TestExistClosure` | ||
972 | * @param hc the key of the query | ||
973 | * @param value the existing `struct PeerRequest`. | ||
974 | * @return #GNUNET_YES to continue to iterate, | ||
975 | * #GNUNET_NO if we successfully merged | ||
976 | */ | ||
977 | static int | ||
978 | test_exist_cb (void *cls, | ||
979 | const struct GNUNET_HashCode *hc, | ||
980 | void *value) | ||
981 | { | ||
982 | struct TestExistClosure *tec = cls; | ||
983 | struct PeerRequest *peerreq = value; | ||
984 | struct GSF_PendingRequest *pr; | ||
985 | struct GSF_PendingRequestData *prd; | ||
986 | |||
987 | pr = peerreq->pr; | ||
988 | prd = GSF_pending_request_get_data_ (pr); | ||
989 | if (prd->type != tec->type) | ||
990 | return GNUNET_YES; | ||
991 | if (prd->ttl.abs_value_us >= | ||
992 | GNUNET_TIME_absolute_get ().abs_value_us + tec->ttl * 1000LL) | ||
993 | { | ||
994 | /* existing request has higher TTL, drop new one! */ | ||
995 | prd->priority += tec->priority; | ||
996 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
997 | "Have existing request with higher TTL, dropping new request.\n"); | ||
998 | GNUNET_STATISTICS_update (GSF_stats, | ||
999 | gettext_noop | ||
1000 | ("# requests dropped due to higher-TTL request"), | ||
1001 | 1, GNUNET_NO); | ||
1002 | tec->finished = GNUNET_YES; | ||
1003 | return GNUNET_NO; | ||
1004 | } | ||
1005 | /* existing request has lower TTL, drop old one! */ | ||
1006 | tec->priority += prd->priority; | ||
1007 | free_pending_request (peerreq); | ||
1008 | GSF_pending_request_cancel_ (pr, | ||
1009 | GNUNET_YES); | ||
1010 | return GNUNET_NO; | ||
1011 | } | ||
1012 | |||
1013 | |||
1014 | /** | ||
1015 | * Handle P2P "QUERY" message. Creates the pending request entry | ||
1016 | * and sets up all of the data structures to that we will | ||
1017 | * process replies properly. Does not initiate forwarding or | ||
1018 | * local database lookups. | ||
1019 | * | ||
1020 | * @param cls the other peer involved (sender of the message) | ||
1021 | * @param gm the GET message | ||
1022 | */ | ||
1023 | void | ||
1024 | handle_p2p_get (void *cls, | ||
1025 | const struct GetMessage *gm) | ||
1026 | { | ||
1027 | struct GSF_ConnectedPeer *cps = cls; | ||
1028 | struct PeerRequest *peerreq; | ||
1029 | struct GSF_PendingRequest *pr; | ||
1030 | struct GSF_ConnectedPeer *cp; | ||
1031 | const struct GNUNET_PeerIdentity *target; | ||
1032 | enum GSF_PendingRequestOptions options; | ||
1033 | uint16_t msize; | ||
1034 | unsigned int bits; | ||
1035 | const struct GNUNET_PeerIdentity *opt; | ||
1036 | uint32_t bm; | ||
1037 | size_t bfsize; | ||
1038 | uint32_t ttl_decrement; | ||
1039 | struct TestExistClosure tec; | ||
1040 | GNUNET_PEER_Id spid; | ||
1041 | const struct GSF_PendingRequestData *prd; | ||
1042 | |||
1043 | msize = ntohs (gm->header.size); | ||
1044 | tec.type = ntohl (gm->type); | ||
1045 | bm = ntohl (gm->hash_bitmap); | ||
1046 | bits = 0; | ||
1047 | while (bm > 0) | ||
1048 | { | ||
1049 | if (1 == (bm & 1)) | ||
1050 | bits++; | ||
1051 | bm >>= 1; | ||
1052 | } | ||
1053 | opt = (const struct GNUNET_PeerIdentity *) &gm[1]; | ||
1054 | bfsize = msize - sizeof(struct GetMessage) - bits * sizeof(struct | ||
1055 | GNUNET_PeerIdentity); | ||
1056 | GNUNET_STATISTICS_update (GSF_stats, | ||
1057 | gettext_noop | ||
1058 | ("# GET requests received (from other peers)"), | ||
1059 | 1, | ||
1060 | GNUNET_NO); | ||
1061 | GSF_cover_query_count++; | ||
1062 | bm = ntohl (gm->hash_bitmap); | ||
1063 | bits = 0; | ||
1064 | if (0 != (bm & GET_MESSAGE_BIT_RETURN_TO)) | ||
1065 | cp = GSF_peer_get_ (&opt[bits++]); | ||
1066 | else | ||
1067 | cp = cps; | ||
1068 | if (NULL == cp) | ||
1069 | { | ||
1070 | if (0 != (bm & GET_MESSAGE_BIT_RETURN_TO)) | ||
1071 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1072 | "Failed to find RETURN-TO peer `%s' in connection set. Dropping query.\n", | ||
1073 | GNUNET_i2s (&opt[bits - 1])); | ||
1074 | |||
1075 | else | ||
1076 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1077 | "Failed to find peer `%s' in connection set. Dropping query.\n", | ||
1078 | GNUNET_i2s (cps->ppd.peer)); | ||
1079 | GNUNET_STATISTICS_update (GSF_stats, | ||
1080 | gettext_noop | ||
1081 | ( | ||
1082 | "# requests dropped due to missing reverse route"), | ||
1083 | 1, | ||
1084 | GNUNET_NO); | ||
1085 | return; | ||
1086 | } | ||
1087 | unsigned int queue_size = GNUNET_MQ_get_length (cp->mq); | ||
1088 | queue_size += cp->ppd.pending_replies + cp->delay_queue_size; | ||
1089 | if (queue_size > MAX_QUEUE_PER_PEER) | ||
1090 | { | ||
1091 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1092 | "Peer `%s' has too many replies queued already. Dropping query.\n", | ||
1093 | GNUNET_i2s (cps->ppd.peer)); | ||
1094 | GNUNET_STATISTICS_update (GSF_stats, | ||
1095 | gettext_noop ( | ||
1096 | "# requests dropped due to full reply queue"), | ||
1097 | 1, | ||
1098 | GNUNET_NO); | ||
1099 | return; | ||
1100 | } | ||
1101 | /* note that we can really only check load here since otherwise | ||
1102 | * peers could find out that we are overloaded by not being | ||
1103 | * disconnected after sending us a malformed query... */ | ||
1104 | tec.priority = bound_priority (ntohl (gm->priority), | ||
1105 | cps); | ||
1106 | if (tec.priority < 0) | ||
1107 | { | ||
1108 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1109 | "Dropping query from `%s', this peer is too busy.\n", | ||
1110 | GNUNET_i2s (cps->ppd.peer)); | ||
1111 | return; | ||
1112 | } | ||
1113 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1114 | "Received request for `%s' of type %u from peer `%s' with flags %u\n", | ||
1115 | GNUNET_h2s (&gm->query), | ||
1116 | (unsigned int) tec.type, | ||
1117 | GNUNET_i2s (cps->ppd.peer), | ||
1118 | (unsigned int) bm); | ||
1119 | target = | ||
1120 | (0 != | ||
1121 | (bm & GET_MESSAGE_BIT_TRANSMIT_TO)) ? (&opt[bits++]) : NULL; | ||
1122 | options = GSF_PRO_DEFAULTS; | ||
1123 | spid = 0; | ||
1124 | if ((GNUNET_LOAD_get_load (cp->ppd.transmission_delay) > 3 * (1 | ||
1125 | + tec.priority)) | ||
1126 | || (GNUNET_LOAD_get_average (cp->ppd.transmission_delay) > | ||
1127 | GNUNET_CONSTANTS_MAX_CORK_DELAY.rel_value_us * 2 | ||
1128 | + GNUNET_LOAD_get_average (GSF_rt_entry_lifetime))) | ||
1129 | { | ||
1130 | /* don't have BW to send to peer, or would likely take longer than we have for it, | ||
1131 | * so at best indirect the query */ | ||
1132 | tec.priority = 0; | ||
1133 | options |= GSF_PRO_FORWARD_ONLY; | ||
1134 | spid = GNUNET_PEER_intern (cps->ppd.peer); | ||
1135 | GNUNET_assert (0 != spid); | ||
1136 | } | ||
1137 | tec.ttl = bound_ttl (ntohl (gm->ttl), | ||
1138 | tec.priority); | ||
1139 | /* decrement ttl (always) */ | ||
1140 | ttl_decrement = | ||
1141 | 2 * TTL_DECREMENT + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1142 | TTL_DECREMENT); | ||
1143 | if ((tec.ttl < 0) && | ||
1144 | (((int32_t) (tec.ttl - ttl_decrement)) > 0)) | ||
1145 | { | ||
1146 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1147 | "Dropping query from `%s' due to TTL underflow (%d - %u).\n", | ||
1148 | GNUNET_i2s (cps->ppd.peer), | ||
1149 | tec.ttl, | ||
1150 | ttl_decrement); | ||
1151 | GNUNET_STATISTICS_update (GSF_stats, | ||
1152 | gettext_noop | ||
1153 | ("# requests dropped due TTL underflow"), 1, | ||
1154 | GNUNET_NO); | ||
1155 | /* integer underflow => drop (should be very rare)! */ | ||
1156 | return; | ||
1157 | } | ||
1158 | tec.ttl -= ttl_decrement; | ||
1159 | |||
1160 | /* test if the request already exists */ | ||
1161 | tec.finished = GNUNET_NO; | ||
1162 | GNUNET_CONTAINER_multihashmap_get_multiple (cp->request_map, | ||
1163 | &gm->query, | ||
1164 | &test_exist_cb, | ||
1165 | &tec); | ||
1166 | if (GNUNET_YES == tec.finished) | ||
1167 | return; /* merged into existing request, we're done */ | ||
1168 | |||
1169 | peerreq = GNUNET_new (struct PeerRequest); | ||
1170 | peerreq->cp = cp; | ||
1171 | pr = GSF_pending_request_create_ (options, | ||
1172 | tec.type, | ||
1173 | &gm->query, | ||
1174 | target, | ||
1175 | (bfsize > 0) | ||
1176 | ? (const char *) &opt[bits] | ||
1177 | : NULL, | ||
1178 | bfsize, | ||
1179 | 1 /* anonymity */, | ||
1180 | (uint32_t) tec.priority, | ||
1181 | tec.ttl, | ||
1182 | spid, | ||
1183 | GNUNET_PEER_intern (cps->ppd.peer), | ||
1184 | NULL, 0, /* replies_seen */ | ||
1185 | &handle_p2p_reply, | ||
1186 | peerreq); | ||
1187 | GNUNET_assert (NULL != pr); | ||
1188 | prd = GSF_pending_request_get_data_ (pr); | ||
1189 | peerreq->pr = pr; | ||
1190 | GNUNET_break (GNUNET_OK == | ||
1191 | GNUNET_CONTAINER_multihashmap_put (cp->request_map, | ||
1192 | &prd->query, | ||
1193 | peerreq, | ||
1194 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); | ||
1195 | GNUNET_STATISTICS_update (GSF_stats, | ||
1196 | gettext_noop ( | ||
1197 | "# P2P query messages received and processed"), | ||
1198 | 1, | ||
1199 | GNUNET_NO); | ||
1200 | GNUNET_STATISTICS_update (GSF_stats, | ||
1201 | gettext_noop ("# P2P searches active"), | ||
1202 | 1, | ||
1203 | GNUNET_NO); | ||
1204 | GSF_pending_request_get_data_ (pr)->has_started = GNUNET_YES; | ||
1205 | GSF_local_lookup_ (pr, | ||
1206 | &GSF_consider_forwarding, | ||
1207 | NULL); | ||
1208 | } | ||
1209 | |||
1210 | |||
1211 | /** | ||
1212 | * Transmit a message to the given peer as soon as possible. | ||
1213 | * If the peer disconnects before the transmission can happen, | ||
1214 | * the callback is invoked with a `NULL` @a buffer. | ||
1215 | * | ||
1216 | * @param cp target peer | ||
1217 | * @param is_query is this a query (#GNUNET_YES) or content (#GNUNET_NO) or neither (#GNUNET_SYSERR) | ||
1218 | * @param priority how important is this request? | ||
1219 | * @param env message to send | ||
1220 | */ | ||
1221 | void | ||
1222 | GSF_peer_transmit_ (struct GSF_ConnectedPeer *cp, | ||
1223 | int is_query, | ||
1224 | uint32_t priority, | ||
1225 | struct GNUNET_MQ_Envelope *env) | ||
1226 | { | ||
1227 | struct GSF_PeerTransmitHandle *pth; | ||
1228 | struct GSF_PeerTransmitHandle *pos; | ||
1229 | struct GSF_PeerTransmitHandle *prev; | ||
1230 | |||
1231 | pth = GNUNET_new (struct GSF_PeerTransmitHandle); | ||
1232 | pth->transmission_request_start_time = GNUNET_TIME_absolute_get (); | ||
1233 | pth->env = env; | ||
1234 | pth->is_query = is_query; | ||
1235 | pth->priority = priority; | ||
1236 | pth->cp = cp; | ||
1237 | /* insertion sort (by priority, descending) */ | ||
1238 | prev = NULL; | ||
1239 | pos = cp->pth_head; | ||
1240 | while ((NULL != pos) && (pos->priority > priority)) | ||
1241 | { | ||
1242 | prev = pos; | ||
1243 | pos = pos->next; | ||
1244 | } | ||
1245 | GNUNET_CONTAINER_DLL_insert_after (cp->pth_head, | ||
1246 | cp->pth_tail, | ||
1247 | prev, | ||
1248 | pth); | ||
1249 | if (GNUNET_YES == is_query) | ||
1250 | cp->ppd.pending_queries++; | ||
1251 | else if (GNUNET_NO == is_query) | ||
1252 | cp->ppd.pending_replies++; | ||
1253 | schedule_transmission (pth); | ||
1254 | } | ||
1255 | |||
1256 | |||
1257 | /** | ||
1258 | * Report on receiving a reply; update the performance record of the given peer. | ||
1259 | * | ||
1260 | * @param cp responding peer (will be updated) | ||
1261 | * @param request_time time at which the original query was transmitted | ||
1262 | * @param request_priority priority of the original request | ||
1263 | */ | ||
1264 | void | ||
1265 | GSF_peer_update_performance_ (struct GSF_ConnectedPeer *cp, | ||
1266 | struct GNUNET_TIME_Absolute request_time, | ||
1267 | uint32_t request_priority) | ||
1268 | { | ||
1269 | struct GNUNET_TIME_Relative delay; | ||
1270 | |||
1271 | delay = GNUNET_TIME_absolute_get_duration (request_time); | ||
1272 | cp->ppd.avg_reply_delay.rel_value_us = | ||
1273 | (cp->ppd.avg_reply_delay.rel_value_us * (RUNAVG_DELAY_N - 1) | ||
1274 | + delay.rel_value_us) / RUNAVG_DELAY_N; | ||
1275 | cp->ppd.avg_priority = | ||
1276 | (cp->ppd.avg_priority * (RUNAVG_DELAY_N - 1) | ||
1277 | + request_priority) / RUNAVG_DELAY_N; | ||
1278 | } | ||
1279 | |||
1280 | |||
1281 | /** | ||
1282 | * Report on receiving a reply in response to an initiating client. | ||
1283 | * Remember that this peer is good for this client. | ||
1284 | * | ||
1285 | * @param cp responding peer (will be updated) | ||
1286 | * @param initiator_client local client on responsible for query | ||
1287 | */ | ||
1288 | void | ||
1289 | GSF_peer_update_responder_client_ (struct GSF_ConnectedPeer *cp, | ||
1290 | struct GSF_LocalClient *initiator_client) | ||
1291 | { | ||
1292 | cp->ppd.last_client_replies[cp->last_client_replies_woff++ | ||
1293 | % CS2P_SUCCESS_LIST_SIZE] = initiator_client; | ||
1294 | } | ||
1295 | |||
1296 | |||
1297 | /** | ||
1298 | * Report on receiving a reply in response to an initiating peer. | ||
1299 | * Remember that this peer is good for this initiating peer. | ||
1300 | * | ||
1301 | * @param cp responding peer (will be updated) | ||
1302 | * @param initiator_peer other peer responsible for query | ||
1303 | */ | ||
1304 | void | ||
1305 | GSF_peer_update_responder_peer_ (struct GSF_ConnectedPeer *cp, | ||
1306 | const struct GSF_ConnectedPeer *initiator_peer) | ||
1307 | { | ||
1308 | unsigned int woff; | ||
1309 | |||
1310 | woff = cp->last_p2p_replies_woff % P2P_SUCCESS_LIST_SIZE; | ||
1311 | GNUNET_PEER_change_rc (cp->ppd.last_p2p_replies[woff], -1); | ||
1312 | cp->ppd.last_p2p_replies[woff] = initiator_peer->ppd.pid; | ||
1313 | GNUNET_PEER_change_rc (initiator_peer->ppd.pid, 1); | ||
1314 | cp->last_p2p_replies_woff = (woff + 1) % P2P_SUCCESS_LIST_SIZE; | ||
1315 | } | ||
1316 | |||
1317 | |||
1318 | /** | ||
1319 | * Write peer-respect information to a file - flush the buffer entry! | ||
1320 | * | ||
1321 | * @param cls unused | ||
1322 | * @param key peer identity | ||
1323 | * @param value the `struct GSF_ConnectedPeer` to flush | ||
1324 | * @return #GNUNET_OK to continue iteration | ||
1325 | */ | ||
1326 | static int | ||
1327 | flush_respect (void *cls, | ||
1328 | const struct GNUNET_PeerIdentity *key, | ||
1329 | void *value) | ||
1330 | { | ||
1331 | struct GSF_ConnectedPeer *cp = value; | ||
1332 | struct GNUNET_PeerIdentity pid; | ||
1333 | |||
1334 | if (cp->ppd.respect == cp->disk_respect) | ||
1335 | return GNUNET_OK; /* unchanged */ | ||
1336 | GNUNET_assert (0 != cp->ppd.pid); | ||
1337 | GNUNET_PEER_resolve (cp->ppd.pid, &pid); | ||
1338 | GNUNET_PEERSTORE_store (peerstore, "fs", &pid, "respect", &cp->ppd.respect, | ||
1339 | sizeof(cp->ppd.respect), | ||
1340 | GNUNET_TIME_UNIT_FOREVER_ABS, | ||
1341 | GNUNET_PEERSTORE_STOREOPTION_REPLACE, | ||
1342 | NULL, | ||
1343 | NULL); | ||
1344 | return GNUNET_OK; | ||
1345 | } | ||
1346 | |||
1347 | |||
1348 | void | ||
1349 | GSF_peer_disconnect_handler (void *cls, | ||
1350 | const struct GNUNET_PeerIdentity *peer, | ||
1351 | void *internal_cls) | ||
1352 | { | ||
1353 | struct GSF_ConnectedPeer *cp = internal_cls; | ||
1354 | struct GSF_PeerTransmitHandle *pth; | ||
1355 | struct GSF_DelayedHandle *dh; | ||
1356 | |||
1357 | if (NULL == cp) | ||
1358 | return; /* must have been disconnect from core with | ||
1359 | * 'peer' == my_id, ignore */ | ||
1360 | flush_respect (NULL, | ||
1361 | peer, | ||
1362 | cp); | ||
1363 | GNUNET_assert (GNUNET_YES == | ||
1364 | GNUNET_CONTAINER_multipeermap_remove (cp_map, | ||
1365 | peer, | ||
1366 | cp)); | ||
1367 | GNUNET_STATISTICS_set (GSF_stats, | ||
1368 | gettext_noop ("# peers connected"), | ||
1369 | GNUNET_CONTAINER_multipeermap_size (cp_map), | ||
1370 | GNUNET_NO); | ||
1371 | if (NULL != cp->respect_iterate_req) | ||
1372 | { | ||
1373 | GNUNET_PEERSTORE_iterate_cancel (cp->respect_iterate_req); | ||
1374 | cp->respect_iterate_req = NULL; | ||
1375 | } | ||
1376 | GNUNET_CONTAINER_multihashmap_iterate (cp->request_map, | ||
1377 | &cancel_pending_request, | ||
1378 | cp); | ||
1379 | GNUNET_CONTAINER_multihashmap_destroy (cp->request_map); | ||
1380 | cp->request_map = NULL; | ||
1381 | GSF_plan_notify_peer_disconnect_ (cp); | ||
1382 | GNUNET_LOAD_value_free (cp->ppd.transmission_delay); | ||
1383 | GNUNET_PEER_decrement_rcs (cp->ppd.last_p2p_replies, | ||
1384 | P2P_SUCCESS_LIST_SIZE); | ||
1385 | memset (cp->ppd.last_p2p_replies, | ||
1386 | 0, | ||
1387 | sizeof(cp->ppd.last_p2p_replies)); | ||
1388 | GSF_push_stop_ (cp); | ||
1389 | while (NULL != (pth = cp->pth_head)) | ||
1390 | { | ||
1391 | GNUNET_CONTAINER_DLL_remove (cp->pth_head, | ||
1392 | cp->pth_tail, | ||
1393 | pth); | ||
1394 | if (GNUNET_YES == pth->is_query) | ||
1395 | GNUNET_assert (0 < cp->ppd.pending_queries--); | ||
1396 | else if (GNUNET_NO == pth->is_query) | ||
1397 | GNUNET_assert (0 < cp->ppd.pending_replies--); | ||
1398 | GNUNET_free (pth); | ||
1399 | } | ||
1400 | while (NULL != (dh = cp->delayed_head)) | ||
1401 | { | ||
1402 | GNUNET_CONTAINER_DLL_remove (cp->delayed_head, | ||
1403 | cp->delayed_tail, | ||
1404 | dh); | ||
1405 | GNUNET_MQ_discard (dh->env); | ||
1406 | cp->delay_queue_size--; | ||
1407 | GNUNET_SCHEDULER_cancel (dh->delay_task); | ||
1408 | GNUNET_free (dh); | ||
1409 | } | ||
1410 | GNUNET_PEER_change_rc (cp->ppd.pid, -1); | ||
1411 | if (NULL != cp->mig_revive_task) | ||
1412 | { | ||
1413 | GNUNET_SCHEDULER_cancel (cp->mig_revive_task); | ||
1414 | cp->mig_revive_task = NULL; | ||
1415 | } | ||
1416 | GNUNET_break (0 == cp->ppd.pending_queries); | ||
1417 | GNUNET_break (0 == cp->ppd.pending_replies); | ||
1418 | GNUNET_free (cp); | ||
1419 | } | ||
1420 | |||
1421 | |||
1422 | /** | ||
1423 | * Closure for #call_iterator(). | ||
1424 | */ | ||
1425 | struct IterationContext | ||
1426 | { | ||
1427 | /** | ||
1428 | * Function to call on each entry. | ||
1429 | */ | ||
1430 | GSF_ConnectedPeerIterator it; | ||
1431 | |||
1432 | /** | ||
1433 | * Closure for @e it. | ||
1434 | */ | ||
1435 | void *it_cls; | ||
1436 | }; | ||
1437 | |||
1438 | |||
1439 | /** | ||
1440 | * Function that calls the callback for each peer. | ||
1441 | * | ||
1442 | * @param cls the `struct IterationContext *` | ||
1443 | * @param key identity of the peer | ||
1444 | * @param value the `struct GSF_ConnectedPeer *` | ||
1445 | * @return #GNUNET_YES to continue iteration | ||
1446 | */ | ||
1447 | static int | ||
1448 | call_iterator (void *cls, | ||
1449 | const struct GNUNET_PeerIdentity *key, | ||
1450 | void *value) | ||
1451 | { | ||
1452 | struct IterationContext *ic = cls; | ||
1453 | struct GSF_ConnectedPeer *cp = value; | ||
1454 | |||
1455 | ic->it (ic->it_cls, | ||
1456 | key, cp, | ||
1457 | &cp->ppd); | ||
1458 | return GNUNET_YES; | ||
1459 | } | ||
1460 | |||
1461 | |||
1462 | void | ||
1463 | GSF_iterate_connected_peers_ (GSF_ConnectedPeerIterator it, | ||
1464 | void *it_cls) | ||
1465 | { | ||
1466 | struct IterationContext ic; | ||
1467 | |||
1468 | ic.it = it; | ||
1469 | ic.it_cls = it_cls; | ||
1470 | GNUNET_CONTAINER_multipeermap_iterate (cp_map, | ||
1471 | &call_iterator, | ||
1472 | &ic); | ||
1473 | } | ||
1474 | |||
1475 | |||
1476 | /** | ||
1477 | * Obtain the identity of a connected peer. | ||
1478 | * | ||
1479 | * @param cp peer to get identity of | ||
1480 | * @param id identity to set (written to) | ||
1481 | */ | ||
1482 | void | ||
1483 | GSF_connected_peer_get_identity_ (const struct GSF_ConnectedPeer *cp, | ||
1484 | struct GNUNET_PeerIdentity *id) | ||
1485 | { | ||
1486 | GNUNET_assert (0 != cp->ppd.pid); | ||
1487 | GNUNET_PEER_resolve (cp->ppd.pid, id); | ||
1488 | } | ||
1489 | |||
1490 | |||
1491 | /** | ||
1492 | * Obtain the identity of a connected peer. | ||
1493 | * | ||
1494 | * @param cp peer to get identity of | ||
1495 | * @return reference to peer identity, valid until peer disconnects (!) | ||
1496 | */ | ||
1497 | const struct GNUNET_PeerIdentity * | ||
1498 | GSF_connected_peer_get_identity2_ (const struct GSF_ConnectedPeer *cp) | ||
1499 | { | ||
1500 | GNUNET_assert (0 != cp->ppd.pid); | ||
1501 | return GNUNET_PEER_resolve2 (cp->ppd.pid); | ||
1502 | } | ||
1503 | |||
1504 | |||
1505 | /** | ||
1506 | * Ask a peer to stop migrating data to us until the given point | ||
1507 | * in time. | ||
1508 | * | ||
1509 | * @param cp peer to ask | ||
1510 | * @param block_time until when to block | ||
1511 | */ | ||
1512 | void | ||
1513 | GSF_block_peer_migration_ (struct GSF_ConnectedPeer *cp, | ||
1514 | struct GNUNET_TIME_Absolute block_time) | ||
1515 | { | ||
1516 | struct GNUNET_MQ_Envelope *env; | ||
1517 | struct MigrationStopMessage *msm; | ||
1518 | |||
1519 | if (cp->last_migration_block.abs_value_us > block_time.abs_value_us) | ||
1520 | { | ||
1521 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1522 | "Migration already blocked for another %s\n", | ||
1523 | GNUNET_STRINGS_relative_time_to_string ( | ||
1524 | GNUNET_TIME_absolute_get_remaining | ||
1525 | (cp-> | ||
1526 | last_migration_block), GNUNET_YES)); | ||
1527 | return; /* already blocked */ | ||
1528 | } | ||
1529 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asking to stop migration for %s\n", | ||
1530 | GNUNET_STRINGS_relative_time_to_string ( | ||
1531 | GNUNET_TIME_absolute_get_remaining (block_time), | ||
1532 | GNUNET_YES)); | ||
1533 | cp->last_migration_block = block_time; | ||
1534 | env = GNUNET_MQ_msg (msm, | ||
1535 | GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP); | ||
1536 | msm->reserved = htonl (0); | ||
1537 | msm->duration | ||
1538 | = GNUNET_TIME_relative_hton (GNUNET_TIME_absolute_get_remaining | ||
1539 | (cp->last_migration_block)); | ||
1540 | GNUNET_STATISTICS_update (GSF_stats, | ||
1541 | gettext_noop ("# migration stop messages sent"), | ||
1542 | 1, | ||
1543 | GNUNET_NO); | ||
1544 | GSF_peer_transmit_ (cp, | ||
1545 | GNUNET_SYSERR, | ||
1546 | UINT32_MAX, | ||
1547 | env); | ||
1548 | } | ||
1549 | |||
1550 | |||
1551 | /** | ||
1552 | * Notify core about a preference we have for the given peer | ||
1553 | * (to allocate more resources towards it). The change will | ||
1554 | * be communicated the next time we reserve bandwidth with | ||
1555 | * core (not instantly). | ||
1556 | * | ||
1557 | * @param cp peer to reserve bandwidth from | ||
1558 | * @param pref preference change | ||
1559 | */ | ||
1560 | void | ||
1561 | GSF_connected_peer_change_preference_ (struct GSF_ConnectedPeer *cp, | ||
1562 | uint64_t pref) | ||
1563 | { | ||
1564 | cp->inc_preference += pref; | ||
1565 | } | ||
1566 | |||
1567 | |||
1568 | /** | ||
1569 | * Call this method periodically to flush respect information to disk. | ||
1570 | * | ||
1571 | * @param cls closure, not used | ||
1572 | */ | ||
1573 | static void | ||
1574 | cron_flush_respect (void *cls) | ||
1575 | { | ||
1576 | fr_task = NULL; | ||
1577 | GNUNET_CONTAINER_multipeermap_iterate (cp_map, | ||
1578 | &flush_respect, | ||
1579 | NULL); | ||
1580 | fr_task = GNUNET_SCHEDULER_add_delayed_with_priority (RESPECT_FLUSH_FREQ, | ||
1581 | GNUNET_SCHEDULER_PRIORITY_HIGH, | ||
1582 | &cron_flush_respect, | ||
1583 | NULL); | ||
1584 | } | ||
1585 | |||
1586 | |||
1587 | /** | ||
1588 | * Initialize peer management subsystem. | ||
1589 | */ | ||
1590 | void | ||
1591 | GSF_connected_peer_init_ () | ||
1592 | { | ||
1593 | cp_map = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES); | ||
1594 | peerstore = GNUNET_PEERSTORE_connect (GSF_cfg); | ||
1595 | fr_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_HIGH, | ||
1596 | &cron_flush_respect, NULL); | ||
1597 | } | ||
1598 | |||
1599 | |||
1600 | /** | ||
1601 | * Shutdown peer management subsystem. | ||
1602 | */ | ||
1603 | void | ||
1604 | GSF_connected_peer_done_ () | ||
1605 | { | ||
1606 | GNUNET_CONTAINER_multipeermap_iterate (cp_map, | ||
1607 | &flush_respect, | ||
1608 | NULL); | ||
1609 | GNUNET_SCHEDULER_cancel (fr_task); | ||
1610 | fr_task = NULL; | ||
1611 | GNUNET_CONTAINER_multipeermap_destroy (cp_map); | ||
1612 | cp_map = NULL; | ||
1613 | GNUNET_PEERSTORE_disconnect (peerstore, | ||
1614 | GNUNET_YES); | ||
1615 | } | ||
1616 | |||
1617 | |||
1618 | /** | ||
1619 | * Iterator to remove references to LC entry. | ||
1620 | * | ||
1621 | * @param cls the `struct GSF_LocalClient *` to look for | ||
1622 | * @param key current key code | ||
1623 | * @param value value in the hash map (peer entry) | ||
1624 | * @return #GNUNET_YES (we should continue to iterate) | ||
1625 | */ | ||
1626 | static int | ||
1627 | clean_local_client (void *cls, | ||
1628 | const struct GNUNET_PeerIdentity *key, | ||
1629 | void *value) | ||
1630 | { | ||
1631 | const struct GSF_LocalClient *lc = cls; | ||
1632 | struct GSF_ConnectedPeer *cp = value; | ||
1633 | unsigned int i; | ||
1634 | |||
1635 | for (i = 0; i < CS2P_SUCCESS_LIST_SIZE; i++) | ||
1636 | if (cp->ppd.last_client_replies[i] == lc) | ||
1637 | cp->ppd.last_client_replies[i] = NULL; | ||
1638 | return GNUNET_YES; | ||
1639 | } | ||
1640 | |||
1641 | |||
1642 | /** | ||
1643 | * Notification that a local client disconnected. Clean up all of our | ||
1644 | * references to the given handle. | ||
1645 | * | ||
1646 | * @param lc handle to the local client (henceforth invalid) | ||
1647 | */ | ||
1648 | void | ||
1649 | GSF_handle_local_client_disconnect_ (const struct GSF_LocalClient *lc) | ||
1650 | { | ||
1651 | if (NULL == cp_map) | ||
1652 | return; /* already cleaned up */ | ||
1653 | GNUNET_CONTAINER_multipeermap_iterate (cp_map, | ||
1654 | &clean_local_client, | ||
1655 | (void *) lc); | ||
1656 | } | ||
1657 | |||
1658 | |||
1659 | /* 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 cea82b10c..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 | */ | ||
63 | struct 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 | */ | ||
156 | typedef 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 | */ | ||
171 | typedef 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 | */ | ||
184 | typedef 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 | */ | ||
193 | struct 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 message queue for talking to @a peer | ||
203 | * @return internal handle for the peer | ||
204 | */ | ||
205 | void * | ||
206 | GSF_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 | */ | ||
217 | struct GSF_ConnectedPeer * | ||
218 | GSF_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 | */ | ||
227 | void | ||
228 | GSF_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 | */ | ||
242 | void | ||
243 | GSF_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 | */ | ||
256 | void | ||
257 | GSF_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 | */ | ||
269 | void | ||
270 | GSF_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 | */ | ||
281 | void | ||
282 | GSF_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 | */ | ||
293 | void | ||
294 | handle_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 | */ | ||
304 | void | ||
305 | handle_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 | */ | ||
315 | struct GSF_PeerPerformanceData * | ||
316 | GSF_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 | */ | ||
326 | void | ||
327 | GSF_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 disconnected | ||
337 | * @param internal_cls the corresponding `struct GSF_ConnectedPeer` | ||
338 | */ | ||
339 | void | ||
340 | GSF_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 | */ | ||
351 | void | ||
352 | GSF_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 | */ | ||
364 | void | ||
365 | GSF_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 | */ | ||
375 | void | ||
376 | GSF_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 | */ | ||
386 | const struct GNUNET_PeerIdentity * | ||
387 | GSF_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 @a it | ||
395 | */ | ||
396 | void | ||
397 | GSF_iterate_connected_peers_ (GSF_ConnectedPeerIterator it, void *it_cls); | ||
398 | |||
399 | |||
400 | /** | ||
401 | * Initialize peer management subsystem. | ||
402 | */ | ||
403 | void | ||
404 | GSF_connected_peer_init_ (void); | ||
405 | |||
406 | |||
407 | /** | ||
408 | * Shutdown peer management subsystem. | ||
409 | */ | ||
410 | void | ||
411 | GSF_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 8fb34c067..000000000 --- a/src/fs/gnunet-service-fs_indexing.c +++ /dev/null | |||
@@ -1,495 +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_protocols.h" | ||
31 | #include "gnunet_signatures.h" | ||
32 | #include "gnunet_util_lib.h" | ||
33 | #include "gnunet-service-fs.h" | ||
34 | #include "gnunet-service-fs_indexing.h" | ||
35 | #include "fs.h" | ||
36 | |||
37 | /** | ||
38 | * In-memory information about indexed files (also available | ||
39 | * on-disk). | ||
40 | */ | ||
41 | struct IndexInfo | ||
42 | { | ||
43 | /** | ||
44 | * This is a doubly linked list. | ||
45 | */ | ||
46 | struct IndexInfo *next; | ||
47 | |||
48 | /** | ||
49 | * This is a doubly linked list. | ||
50 | */ | ||
51 | struct IndexInfo *prev; | ||
52 | |||
53 | /** | ||
54 | * Name of the indexed file. Memory allocated | ||
55 | * at the end of this struct (do not free). | ||
56 | */ | ||
57 | const char *filename; | ||
58 | |||
59 | /** | ||
60 | * Context for transmitting confirmation to client, | ||
61 | * NULL if we've done this already. | ||
62 | */ | ||
63 | struct GNUNET_SERVER_TransmitContext *tc; | ||
64 | |||
65 | /** | ||
66 | * Context for hashing of the file. | ||
67 | */ | ||
68 | struct GNUNET_CRYPTO_FileHashContext *fhc; | ||
69 | |||
70 | /** | ||
71 | * Hash of the contents of the file. | ||
72 | */ | ||
73 | struct GNUNET_HashCode file_id; | ||
74 | }; | ||
75 | |||
76 | |||
77 | /** | ||
78 | * Head of linked list of indexed files. | ||
79 | * FIXME: we don't need both a DLL and a hashmap here! | ||
80 | */ | ||
81 | static struct IndexInfo *indexed_files_head; | ||
82 | |||
83 | /** | ||
84 | * Tail of linked list of indexed files. | ||
85 | */ | ||
86 | static struct IndexInfo *indexed_files_tail; | ||
87 | |||
88 | /** | ||
89 | * Maps hash over content of indexed files to the respective 'struct IndexInfo'. | ||
90 | * The filenames are pointers into the indexed_files linked list and | ||
91 | * do not need to be freed. | ||
92 | */ | ||
93 | static struct GNUNET_CONTAINER_MultiHashMap *ifm; | ||
94 | |||
95 | /** | ||
96 | * Our configuration. | ||
97 | */ | ||
98 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
99 | |||
100 | /** | ||
101 | * Datastore handle. Created and destroyed by code in | ||
102 | * gnunet-service-fs (this is an alias). | ||
103 | */ | ||
104 | static struct GNUNET_DATASTORE_Handle *dsh; | ||
105 | |||
106 | |||
107 | /** | ||
108 | * Write the current index information list to disk. | ||
109 | */ | ||
110 | static void | ||
111 | write_index_list () | ||
112 | { | ||
113 | struct GNUNET_BIO_WriteHandle *wh; | ||
114 | char *fn; | ||
115 | struct IndexInfo *pos; | ||
116 | |||
117 | if (GNUNET_OK != | ||
118 | GNUNET_CONFIGURATION_get_value_filename (cfg, "FS", "INDEXDB", &fn)) | ||
119 | { | ||
120 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
121 | "fs", | ||
122 | "INDEXDB"); | ||
123 | return; | ||
124 | } | ||
125 | wh = GNUNET_BIO_write_open_file (fn); | ||
126 | if (NULL == wh) | ||
127 | { | ||
128 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
129 | _ ("Could not open `%s'.\n"), | ||
130 | fn); | ||
131 | GNUNET_free (fn); | ||
132 | return; | ||
133 | } | ||
134 | for (pos = indexed_files_head; NULL != pos; pos = pos->next) | ||
135 | if ((GNUNET_OK != GNUNET_BIO_write (wh, | ||
136 | "fs-indexing-file-id", | ||
137 | &pos->file_id, | ||
138 | sizeof(struct GNUNET_HashCode))) || | ||
139 | (GNUNET_OK != GNUNET_BIO_write_string (wh, | ||
140 | "fs-indexing-filename", | ||
141 | pos->filename))) | ||
142 | break; | ||
143 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
144 | { | ||
145 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
146 | _ ("Error writing `%s'.\n"), | ||
147 | fn); | ||
148 | GNUNET_free (fn); | ||
149 | return; | ||
150 | } | ||
151 | GNUNET_free (fn); | ||
152 | } | ||
153 | |||
154 | |||
155 | /** | ||
156 | * Read index information from disk. | ||
157 | */ | ||
158 | static void | ||
159 | read_index_list () | ||
160 | { | ||
161 | struct GNUNET_BIO_ReadHandle *rh; | ||
162 | char *fn; | ||
163 | struct IndexInfo *pos; | ||
164 | char *fname; | ||
165 | struct GNUNET_HashCode hc; | ||
166 | size_t slen; | ||
167 | char *emsg; | ||
168 | |||
169 | if (GNUNET_OK != | ||
170 | GNUNET_CONFIGURATION_get_value_filename (cfg, "FS", "INDEXDB", &fn)) | ||
171 | { | ||
172 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
173 | "fs", | ||
174 | "INDEXDB"); | ||
175 | return; | ||
176 | } | ||
177 | if (GNUNET_NO == GNUNET_DISK_file_test (fn)) | ||
178 | { | ||
179 | /* no index info yet */ | ||
180 | GNUNET_free (fn); | ||
181 | return; | ||
182 | } | ||
183 | rh = GNUNET_BIO_read_open_file (fn); | ||
184 | if (NULL == rh) | ||
185 | { | ||
186 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
187 | _ ("Could not open `%s'.\n"), | ||
188 | fn); | ||
189 | GNUNET_free (fn); | ||
190 | return; | ||
191 | } | ||
192 | while ( | ||
193 | (GNUNET_OK == GNUNET_BIO_read (rh, | ||
194 | "Hash of indexed file", | ||
195 | &hc, | ||
196 | sizeof(struct GNUNET_HashCode))) && | ||
197 | (GNUNET_OK == | ||
198 | GNUNET_BIO_read_string (rh, "Name of indexed file", &fname, 1024 * 16)) && | ||
199 | (fname != NULL)) | ||
200 | { | ||
201 | slen = strlen (fname) + 1; | ||
202 | pos = GNUNET_malloc (sizeof(struct IndexInfo) + slen); | ||
203 | pos->file_id = hc; | ||
204 | pos->filename = (const char *) &pos[1]; | ||
205 | GNUNET_memcpy (&pos[1], fname, slen); | ||
206 | if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put ( | ||
207 | ifm, | ||
208 | &pos->file_id, | ||
209 | pos, | ||
210 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
211 | { | ||
212 | GNUNET_free (pos); | ||
213 | } | ||
214 | else | ||
215 | { | ||
216 | GNUNET_CONTAINER_DLL_insert (indexed_files_head, indexed_files_tail, pos); | ||
217 | } | ||
218 | GNUNET_free (fname); | ||
219 | } | ||
220 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
221 | GNUNET_free (emsg); | ||
222 | GNUNET_free (fn); | ||
223 | } | ||
224 | |||
225 | |||
226 | /** | ||
227 | * Continuation called from datastore's remove | ||
228 | * function. | ||
229 | * | ||
230 | * @param cls unused | ||
231 | * @param success did the deletion work? | ||
232 | * @param min_expiration minimum expiration time required for content to be stored | ||
233 | * @param msg error message | ||
234 | */ | ||
235 | static void | ||
236 | remove_cont (void *cls, | ||
237 | int success, | ||
238 | struct GNUNET_TIME_Absolute min_expiration, | ||
239 | const char *msg) | ||
240 | { | ||
241 | if (GNUNET_OK != success) | ||
242 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
243 | _ ("Failed to delete bogus block: %s\n"), | ||
244 | msg); | ||
245 | } | ||
246 | |||
247 | |||
248 | int | ||
249 | GNUNET_FS_handle_on_demand_block (const struct GNUNET_HashCode *key, | ||
250 | uint32_t size, | ||
251 | const void *data, | ||
252 | enum GNUNET_BLOCK_Type type, | ||
253 | uint32_t priority, | ||
254 | uint32_t anonymity, | ||
255 | uint32_t replication, | ||
256 | struct GNUNET_TIME_Absolute expiration, | ||
257 | uint64_t uid, | ||
258 | GNUNET_DATASTORE_DatumProcessor cont, | ||
259 | void *cont_cls) | ||
260 | { | ||
261 | const struct OnDemandBlock *odb; | ||
262 | struct GNUNET_HashCode nkey; | ||
263 | struct GNUNET_CRYPTO_SymmetricSessionKey skey; | ||
264 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
265 | struct GNUNET_HashCode query; | ||
266 | ssize_t nsize; | ||
267 | char ndata[DBLOCK_SIZE]; | ||
268 | char edata[DBLOCK_SIZE]; | ||
269 | const char *fn; | ||
270 | struct GNUNET_DISK_FileHandle *fh; | ||
271 | uint64_t off; | ||
272 | struct IndexInfo *ii; | ||
273 | |||
274 | if (size != sizeof(struct OnDemandBlock)) | ||
275 | { | ||
276 | GNUNET_break (0); | ||
277 | GNUNET_DATASTORE_remove (dsh, key, size, data, -1, -1, &remove_cont, NULL); | ||
278 | return GNUNET_SYSERR; | ||
279 | } | ||
280 | odb = (const struct OnDemandBlock *) data; | ||
281 | off = GNUNET_ntohll (odb->offset); | ||
282 | ii = GNUNET_CONTAINER_multihashmap_get (ifm, &odb->file_id); | ||
283 | if (NULL == ii) | ||
284 | { | ||
285 | GNUNET_break (0); | ||
286 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
287 | "Failed to find index %s\n", | ||
288 | GNUNET_h2s (&odb->file_id)); | ||
289 | return GNUNET_SYSERR; | ||
290 | } | ||
291 | fn = ii->filename; | ||
292 | if ((NULL == fn) || (0 != access (fn, R_OK))) | ||
293 | { | ||
294 | GNUNET_STATISTICS_update ( | ||
295 | GSF_stats, | ||
296 | gettext_noop ("# index blocks removed: original file inaccessible"), | ||
297 | 1, | ||
298 | GNUNET_YES); | ||
299 | GNUNET_DATASTORE_remove (dsh, key, size, data, -1, -1, &remove_cont, NULL); | ||
300 | return GNUNET_SYSERR; | ||
301 | } | ||
302 | if ((NULL == (fh = GNUNET_DISK_file_open (fn, | ||
303 | GNUNET_DISK_OPEN_READ, | ||
304 | GNUNET_DISK_PERM_NONE))) || | ||
305 | (off != GNUNET_DISK_file_seek (fh, off, GNUNET_DISK_SEEK_SET)) || | ||
306 | (-1 == (nsize = GNUNET_DISK_file_read (fh, ndata, sizeof(ndata))))) | ||
307 | { | ||
308 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
309 | _ ( | ||
310 | "Could not access indexed file `%s' (%s) at offset %llu: %s\n"), | ||
311 | GNUNET_h2s (&odb->file_id), | ||
312 | fn, | ||
313 | (unsigned long long) off, | ||
314 | (fn == NULL) ? _ ("not indexed") : strerror (errno)); | ||
315 | if (fh != NULL) | ||
316 | GNUNET_DISK_file_close (fh); | ||
317 | GNUNET_DATASTORE_remove (dsh, key, size, data, -1, -1, &remove_cont, NULL); | ||
318 | return GNUNET_SYSERR; | ||
319 | } | ||
320 | GNUNET_DISK_file_close (fh); | ||
321 | GNUNET_CRYPTO_hash (ndata, nsize, &nkey); | ||
322 | GNUNET_CRYPTO_hash_to_aes_key (&nkey, &skey, &iv); | ||
323 | GNUNET_CRYPTO_symmetric_encrypt (ndata, nsize, &skey, &iv, edata); | ||
324 | GNUNET_CRYPTO_hash (edata, nsize, &query); | ||
325 | if (0 != memcmp (&query, key, sizeof(struct GNUNET_HashCode))) | ||
326 | { | ||
327 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
328 | _ ("Indexed file `%s' changed at offset %llu\n"), | ||
329 | fn, | ||
330 | (unsigned long long) off); | ||
331 | GNUNET_DATASTORE_remove (dsh, key, size, data, -1, -1, &remove_cont, NULL); | ||
332 | return GNUNET_SYSERR; | ||
333 | } | ||
334 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
335 | "On-demand encoded block for query `%s'\n", | ||
336 | GNUNET_h2s (key)); | ||
337 | cont (cont_cls, | ||
338 | key, | ||
339 | nsize, | ||
340 | edata, | ||
341 | GNUNET_BLOCK_TYPE_FS_DBLOCK, | ||
342 | priority, | ||
343 | anonymity, | ||
344 | replication, | ||
345 | expiration, | ||
346 | uid); | ||
347 | return GNUNET_OK; | ||
348 | } | ||
349 | |||
350 | |||
351 | /** | ||
352 | * Transmit information about indexed files to @a mq. | ||
353 | * | ||
354 | * @param mq message queue to send information to | ||
355 | */ | ||
356 | void | ||
357 | GNUNET_FS_indexing_send_list (struct GNUNET_MQ_Handle *mq) | ||
358 | { | ||
359 | struct GNUNET_MQ_Envelope *env; | ||
360 | struct IndexInfoMessage *iim; | ||
361 | struct GNUNET_MessageHeader *iem; | ||
362 | size_t slen; | ||
363 | const char *fn; | ||
364 | struct IndexInfo *pos; | ||
365 | |||
366 | for (pos = indexed_files_head; NULL != pos; pos = pos->next) | ||
367 | { | ||
368 | fn = pos->filename; | ||
369 | slen = strlen (fn) + 1; | ||
370 | if (slen + sizeof(struct IndexInfoMessage) >= GNUNET_MAX_MESSAGE_SIZE) | ||
371 | { | ||
372 | GNUNET_break (0); | ||
373 | break; | ||
374 | } | ||
375 | env = | ||
376 | GNUNET_MQ_msg_extra (iim, slen, GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY); | ||
377 | iim->reserved = 0; | ||
378 | iim->file_id = pos->file_id; | ||
379 | GNUNET_memcpy (&iim[1], fn, slen); | ||
380 | GNUNET_MQ_send (mq, env); | ||
381 | } | ||
382 | env = GNUNET_MQ_msg (iem, GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END); | ||
383 | GNUNET_MQ_send (mq, env); | ||
384 | } | ||
385 | |||
386 | |||
387 | /** | ||
388 | * Remove a file from the index. | ||
389 | * | ||
390 | * @param fid identifier of the file to remove | ||
391 | * @return #GNUNET_YES if the @a fid was found | ||
392 | */ | ||
393 | int | ||
394 | GNUNET_FS_indexing_do_unindex (const struct GNUNET_HashCode *fid) | ||
395 | { | ||
396 | struct IndexInfo *pos; | ||
397 | |||
398 | for (pos = indexed_files_head; NULL != pos; pos = pos->next) | ||
399 | { | ||
400 | if (0 == memcmp (&pos->file_id, fid, sizeof(struct GNUNET_HashCode))) | ||
401 | { | ||
402 | GNUNET_CONTAINER_DLL_remove (indexed_files_head, indexed_files_tail, pos); | ||
403 | GNUNET_break ( | ||
404 | GNUNET_OK == | ||
405 | GNUNET_CONTAINER_multihashmap_remove (ifm, &pos->file_id, pos)); | ||
406 | GNUNET_free (pos); | ||
407 | write_index_list (); | ||
408 | return GNUNET_YES; | ||
409 | } | ||
410 | } | ||
411 | return GNUNET_NO; | ||
412 | } | ||
413 | |||
414 | |||
415 | /** | ||
416 | * Add the given file to the list of indexed files. | ||
417 | * | ||
418 | * @param filename name of the file | ||
419 | * @param file_id hash identifier for @a filename | ||
420 | */ | ||
421 | void | ||
422 | GNUNET_FS_add_to_index (const char *filename, | ||
423 | const struct GNUNET_HashCode *file_id) | ||
424 | { | ||
425 | struct IndexInfo *ii; | ||
426 | size_t slen; | ||
427 | |||
428 | ii = GNUNET_CONTAINER_multihashmap_get (ifm, file_id); | ||
429 | if (NULL != ii) | ||
430 | { | ||
431 | GNUNET_log ( | ||
432 | GNUNET_ERROR_TYPE_INFO, | ||
433 | _ ( | ||
434 | "Index request received for file `%s' is already indexed as `%s'. Permitting anyway.\n"), | ||
435 | filename, | ||
436 | ii->filename); | ||
437 | return; | ||
438 | } | ||
439 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
440 | "Adding file %s to index as %s\n", | ||
441 | filename, | ||
442 | GNUNET_h2s (file_id)); | ||
443 | slen = strlen (filename) + 1; | ||
444 | ii = GNUNET_malloc (sizeof(struct IndexInfo) + slen); | ||
445 | ii->file_id = *file_id; | ||
446 | ii->filename = (const char *) &ii[1]; | ||
447 | GNUNET_memcpy (&ii[1], filename, slen); | ||
448 | GNUNET_CONTAINER_DLL_insert (indexed_files_head, indexed_files_tail, ii); | ||
449 | GNUNET_assert (GNUNET_OK == | ||
450 | GNUNET_CONTAINER_multihashmap_put ( | ||
451 | ifm, | ||
452 | &ii->file_id, | ||
453 | ii, | ||
454 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
455 | write_index_list (); | ||
456 | } | ||
457 | |||
458 | |||
459 | /** | ||
460 | * Shutdown the module. | ||
461 | */ | ||
462 | void | ||
463 | GNUNET_FS_indexing_done () | ||
464 | { | ||
465 | struct IndexInfo *pos; | ||
466 | |||
467 | while (NULL != (pos = indexed_files_head)) | ||
468 | { | ||
469 | GNUNET_CONTAINER_DLL_remove (indexed_files_head, indexed_files_tail, pos); | ||
470 | if (pos->fhc != NULL) | ||
471 | GNUNET_CRYPTO_hash_file_cancel (pos->fhc); | ||
472 | GNUNET_break ( | ||
473 | GNUNET_OK == | ||
474 | GNUNET_CONTAINER_multihashmap_remove (ifm, &pos->file_id, pos)); | ||
475 | GNUNET_free (pos); | ||
476 | } | ||
477 | GNUNET_CONTAINER_multihashmap_destroy (ifm); | ||
478 | ifm = NULL; | ||
479 | cfg = NULL; | ||
480 | } | ||
481 | |||
482 | |||
483 | int | ||
484 | GNUNET_FS_indexing_init (const struct GNUNET_CONFIGURATION_Handle *c, | ||
485 | struct GNUNET_DATASTORE_Handle *d) | ||
486 | { | ||
487 | cfg = c; | ||
488 | dsh = d; | ||
489 | ifm = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_YES); | ||
490 | read_index_list (); | ||
491 | return GNUNET_OK; | ||
492 | } | ||
493 | |||
494 | |||
495 | /* 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 244a51900..000000000 --- a/src/fs/gnunet-service-fs_indexing.h +++ /dev/null | |||
@@ -1,120 +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_protocols.h" | ||
33 | #include "gnunet_signatures.h" | ||
34 | #include "gnunet_util_lib.h" | ||
35 | |||
36 | |||
37 | /** | ||
38 | * We've received an on-demand encoded block from the datastore. | ||
39 | * Attempt to do on-demand encoding and (if successful), call the | ||
40 | * continuation with the resulting block. On error, clean up and ask | ||
41 | * the datastore for more results. | ||
42 | * | ||
43 | * @param key key for the content | ||
44 | * @param size number of bytes in data | ||
45 | * @param data content stored | ||
46 | * @param type type of the content | ||
47 | * @param priority priority of the content | ||
48 | * @param anonymity anonymity-level for the content | ||
49 | * @param replication replication-level for the content | ||
50 | * @param expiration expiration time for the content | ||
51 | * @param uid unique identifier for the datum; | ||
52 | * maybe 0 if no unique identifier is available | ||
53 | * @param cont function to call with the actual block (at most once, on success) | ||
54 | * @param cont_cls closure for @a cont | ||
55 | * @return #GNUNET_OK on success | ||
56 | */ | ||
57 | int | ||
58 | GNUNET_FS_handle_on_demand_block (const struct GNUNET_HashCode *key, | ||
59 | uint32_t size, | ||
60 | const void *data, | ||
61 | enum GNUNET_BLOCK_Type type, | ||
62 | uint32_t priority, | ||
63 | uint32_t anonymity, | ||
64 | uint32_t replication, | ||
65 | struct GNUNET_TIME_Absolute expiration, | ||
66 | uint64_t uid, | ||
67 | GNUNET_DATASTORE_DatumProcessor cont, | ||
68 | void *cont_cls); | ||
69 | |||
70 | |||
71 | /** | ||
72 | * Transmit information about indexed files to @a mq. | ||
73 | * | ||
74 | * @param mq message queue to send information to | ||
75 | */ | ||
76 | void | ||
77 | GNUNET_FS_indexing_send_list (struct GNUNET_MQ_Handle *mq); | ||
78 | |||
79 | |||
80 | /** | ||
81 | * Remove a file from the index. | ||
82 | * | ||
83 | * @param fid identifier of the file to remove | ||
84 | * @return #GNUNET_YES if the @a fid was found | ||
85 | */ | ||
86 | int | ||
87 | GNUNET_FS_indexing_do_unindex (const struct GNUNET_HashCode *fid); | ||
88 | |||
89 | |||
90 | /** | ||
91 | * Add the given file to the list of indexed files. | ||
92 | * | ||
93 | * @param filename name of the file | ||
94 | * @param file_id hash identifier for @a filename | ||
95 | */ | ||
96 | void | ||
97 | GNUNET_FS_add_to_index (const char *filename, | ||
98 | const struct GNUNET_HashCode *file_id); | ||
99 | |||
100 | |||
101 | /** | ||
102 | * Initialize the indexing submodule. | ||
103 | * | ||
104 | * @param c configuration to use | ||
105 | * @param d datastore to use | ||
106 | * @return GNUNET_OK on success | ||
107 | */ | ||
108 | int | ||
109 | GNUNET_FS_indexing_init (const struct GNUNET_CONFIGURATION_Handle *c, | ||
110 | struct GNUNET_DATASTORE_Handle *d); | ||
111 | |||
112 | |||
113 | /** | ||
114 | * Shutdown the module. | ||
115 | */ | ||
116 | void | ||
117 | GNUNET_FS_indexing_done (void); | ||
118 | |||
119 | |||
120 | #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 | */ | ||
41 | struct PendingRequestList; | ||
42 | |||
43 | /** | ||
44 | * Transmission plan for a peer. | ||
45 | */ | ||
46 | struct 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 | */ | ||
66 | struct 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 | */ | ||
108 | struct 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 | */ | ||
167 | struct 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 | */ | ||
207 | static struct GNUNET_CONTAINER_MultiPeerMap *plans; | ||
208 | |||
209 | /** | ||
210 | * Sum of all transmission counters (equals total delay for all plan entries). | ||
211 | */ | ||
212 | static unsigned long long total_delay; | ||
213 | |||
214 | /** | ||
215 | * Number of plan entries. | ||
216 | */ | ||
217 | static 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 | */ | ||
232 | static const struct GNUNET_HashCode * | ||
233 | get_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 | */ | ||
245 | static void | ||
246 | plan (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 | */ | ||
345 | struct GSF_PendingRequest * | ||
346 | get_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 | */ | ||
378 | static void | ||
379 | schedule_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 | */ | ||
473 | struct 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 | */ | ||
497 | static int | ||
498 | merge_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 | */ | ||
556 | void | ||
557 | GSF_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 | */ | ||
636 | void | ||
637 | GSF_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 | */ | ||
721 | int | ||
722 | GSF_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 | */ | ||
753 | void | ||
754 | GSF_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 | */ | ||
795 | void | ||
796 | GSF_plan_init () | ||
797 | { | ||
798 | plans = GNUNET_CONTAINER_multipeermap_create (256, | ||
799 | GNUNET_YES); | ||
800 | } | ||
801 | |||
802 | |||
803 | /** | ||
804 | * Shutdown plan subsystem. | ||
805 | */ | ||
806 | void | ||
807 | GSF_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 | */ | ||
38 | void | ||
39 | GSF_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 | */ | ||
49 | void | ||
50 | GSF_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 | */ | ||
59 | void | ||
60 | GSF_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 | */ | ||
71 | int | ||
72 | GSF_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 | */ | ||
83 | void | ||
84 | GSF_plan_init (void); | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Shutdown plan subsystem. | ||
89 | */ | ||
90 | void | ||
91 | GSF_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 f192c017d..000000000 --- a/src/fs/gnunet-service-fs_pr.c +++ /dev/null | |||
@@ -1,1887 +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 | */ | ||
77 | struct 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 | */ | ||
205 | static struct GNUNET_CONTAINER_MultiHashMap *pr_map; | ||
206 | |||
207 | |||
208 | /** | ||
209 | * Datastore 'PUT' load tracking. | ||
210 | */ | ||
211 | static struct GNUNET_LOAD_Value *datastore_put_load; | ||
212 | |||
213 | |||
214 | /** | ||
215 | * Are we allowed to migrate content to this peer. | ||
216 | */ | ||
217 | static 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 | */ | ||
228 | static 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 | */ | ||
236 | static 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 | */ | ||
249 | static void | ||
250 | refresh_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 | struct GSF_PendingRequest * | ||
278 | GSF_pending_request_create_ (enum GSF_PendingRequestOptions options, | ||
279 | enum GNUNET_BLOCK_Type type, | ||
280 | const struct GNUNET_HashCode *query, | ||
281 | const struct GNUNET_PeerIdentity *target, | ||
282 | const char *bf_data, | ||
283 | size_t bf_size, | ||
284 | uint32_t anonymity_level, | ||
285 | uint32_t priority, | ||
286 | int32_t ttl, | ||
287 | GNUNET_PEER_Id sender_pid, | ||
288 | GNUNET_PEER_Id origin_pid, | ||
289 | const struct GNUNET_HashCode *replies_seen, | ||
290 | unsigned int replies_seen_count, | ||
291 | GSF_PendingRequestReplyHandler rh, | ||
292 | void *rh_cls) | ||
293 | { | ||
294 | struct GSF_PendingRequest *pr; | ||
295 | struct GSF_PendingRequest *dpr; | ||
296 | size_t extra; | ||
297 | struct GNUNET_HashCode *eptr; | ||
298 | |||
299 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
300 | "Creating request handle for `%s' of type %d\n", | ||
301 | GNUNET_h2s (query), | ||
302 | type); | ||
303 | #if INSANE_STATISTICS | ||
304 | GNUNET_STATISTICS_update (GSF_stats, | ||
305 | gettext_noop ("# Pending requests created"), | ||
306 | 1, | ||
307 | GNUNET_NO); | ||
308 | #endif | ||
309 | extra = 0; | ||
310 | if (NULL != target) | ||
311 | extra += sizeof(struct GNUNET_PeerIdentity); | ||
312 | pr = GNUNET_malloc (sizeof(struct GSF_PendingRequest) + extra); | ||
313 | pr->public_data.query = *query; | ||
314 | eptr = (struct GNUNET_HashCode *) &pr[1]; | ||
315 | if (NULL != target) | ||
316 | { | ||
317 | pr->public_data.target = (struct GNUNET_PeerIdentity *) eptr; | ||
318 | GNUNET_memcpy (eptr, target, sizeof(struct GNUNET_PeerIdentity)); | ||
319 | } | ||
320 | pr->public_data.anonymity_level = anonymity_level; | ||
321 | pr->public_data.priority = priority; | ||
322 | pr->public_data.original_priority = priority; | ||
323 | pr->public_data.options = options; | ||
324 | pr->public_data.type = type; | ||
325 | pr->public_data.start_time = GNUNET_TIME_absolute_get (); | ||
326 | pr->sender_pid = sender_pid; | ||
327 | pr->origin_pid = origin_pid; | ||
328 | pr->rh = rh; | ||
329 | pr->rh_cls = rh_cls; | ||
330 | GNUNET_assert ((sender_pid != 0) || (0 == (options & GSF_PRO_FORWARD_ONLY))); | ||
331 | if (ttl >= 0) | ||
332 | pr->public_data.ttl = GNUNET_TIME_relative_to_absolute ( | ||
333 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, (uint32_t) ttl)); | ||
334 | else | ||
335 | pr->public_data.ttl = GNUNET_TIME_absolute_subtract ( | ||
336 | pr->public_data.start_time, | ||
337 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, | ||
338 | (uint32_t) (-ttl))); | ||
339 | if (replies_seen_count > 0) | ||
340 | { | ||
341 | pr->replies_seen_size = replies_seen_count; | ||
342 | pr->replies_seen = | ||
343 | GNUNET_new_array (pr->replies_seen_size, struct GNUNET_HashCode); | ||
344 | GNUNET_memcpy (pr->replies_seen, | ||
345 | replies_seen, | ||
346 | replies_seen_count * sizeof(struct GNUNET_HashCode)); | ||
347 | pr->replies_seen_count = replies_seen_count; | ||
348 | } | ||
349 | if ((NULL != bf_data) && | ||
350 | (GNUNET_BLOCK_TYPE_FS_UBLOCK == pr->public_data.type)) | ||
351 | { | ||
352 | pr->bg = GNUNET_BLOCK_group_create (GSF_block_ctx, | ||
353 | pr->public_data.type, | ||
354 | bf_data, | ||
355 | bf_size, | ||
356 | "seen-set-size", | ||
357 | 0, | ||
358 | NULL); | ||
359 | } | ||
360 | else if ((replies_seen_count > 0) && | ||
361 | (0 != (options & GSF_PRO_BLOOMFILTER_FULL_REFRESH))) | ||
362 | { | ||
363 | refresh_bloomfilter (pr->public_data.type, pr); | ||
364 | } | ||
365 | GNUNET_CONTAINER_multihashmap_put (pr_map, | ||
366 | &pr->public_data.query, | ||
367 | pr, | ||
368 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
369 | if (0 == (options & GSF_PRO_REQUEST_NEVER_EXPIRES)) | ||
370 | { | ||
371 | pr->hnode = GNUNET_CONTAINER_heap_insert (requests_by_expiration_heap, | ||
372 | pr, | ||
373 | pr->public_data.ttl.abs_value_us); | ||
374 | /* make sure we don't track too many requests */ | ||
375 | while (GNUNET_CONTAINER_heap_get_size (requests_by_expiration_heap) > | ||
376 | max_pending_requests) | ||
377 | { | ||
378 | dpr = GNUNET_CONTAINER_heap_peek (requests_by_expiration_heap); | ||
379 | GNUNET_assert (NULL != dpr); | ||
380 | if (pr == dpr) | ||
381 | break; /* let the request live briefly... */ | ||
382 | if (NULL != dpr->rh) | ||
383 | dpr->rh (dpr->rh_cls, | ||
384 | GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED, | ||
385 | dpr, | ||
386 | UINT32_MAX, | ||
387 | GNUNET_TIME_UNIT_FOREVER_ABS, | ||
388 | GNUNET_TIME_UNIT_FOREVER_ABS, | ||
389 | GNUNET_BLOCK_TYPE_ANY, | ||
390 | NULL, | ||
391 | 0); | ||
392 | GSF_pending_request_cancel_ (dpr, GNUNET_YES); | ||
393 | } | ||
394 | } | ||
395 | GNUNET_STATISTICS_update (GSF_stats, | ||
396 | gettext_noop ("# Pending requests active"), | ||
397 | 1, | ||
398 | GNUNET_NO); | ||
399 | return pr; | ||
400 | } | ||
401 | |||
402 | |||
403 | /** | ||
404 | * Obtain the public data associated with a pending request | ||
405 | * | ||
406 | * @param pr pending request | ||
407 | * @return associated public data | ||
408 | */ | ||
409 | struct GSF_PendingRequestData * | ||
410 | GSF_pending_request_get_data_ (struct GSF_PendingRequest *pr) | ||
411 | { | ||
412 | return &pr->public_data; | ||
413 | } | ||
414 | |||
415 | |||
416 | /** | ||
417 | * Test if two pending requests are compatible (would generate | ||
418 | * the same query modulo filters and should thus be processed | ||
419 | * jointly). | ||
420 | * | ||
421 | * @param pra a pending request | ||
422 | * @param prb another pending request | ||
423 | * @return #GNUNET_OK if the requests are compatible | ||
424 | */ | ||
425 | int | ||
426 | GSF_pending_request_is_compatible_ (struct GSF_PendingRequest *pra, | ||
427 | struct GSF_PendingRequest *prb) | ||
428 | { | ||
429 | if ((pra->public_data.type != prb->public_data.type) || | ||
430 | (0 != memcmp (&pra->public_data.query, | ||
431 | &prb->public_data.query, | ||
432 | sizeof(struct GNUNET_HashCode)))) | ||
433 | return GNUNET_NO; | ||
434 | return GNUNET_OK; | ||
435 | } | ||
436 | |||
437 | |||
438 | void | ||
439 | GSF_pending_request_update_ (struct GSF_PendingRequest *pr, | ||
440 | const struct GNUNET_HashCode *replies_seen, | ||
441 | unsigned int replies_seen_count) | ||
442 | { | ||
443 | if (replies_seen_count + pr->replies_seen_count < pr->replies_seen_count) | ||
444 | return; /* integer overflow */ | ||
445 | if (0 != (pr->public_data.options & GSF_PRO_BLOOMFILTER_FULL_REFRESH)) | ||
446 | { | ||
447 | /* we're responsible for the BF, full refresh */ | ||
448 | if (replies_seen_count + pr->replies_seen_count > pr->replies_seen_size) | ||
449 | GNUNET_array_grow (pr->replies_seen, | ||
450 | pr->replies_seen_size, | ||
451 | replies_seen_count + pr->replies_seen_count); | ||
452 | GNUNET_memcpy (&pr->replies_seen[pr->replies_seen_count], | ||
453 | replies_seen, | ||
454 | sizeof(struct GNUNET_HashCode) * replies_seen_count); | ||
455 | pr->replies_seen_count += replies_seen_count; | ||
456 | refresh_bloomfilter (pr->public_data.type, pr); | ||
457 | } | ||
458 | else | ||
459 | { | ||
460 | if (NULL == pr->bg) | ||
461 | { | ||
462 | /* we're not the initiator, but the initiator did not give us | ||
463 | * any bloom-filter, so we need to create one on-the-fly */ | ||
464 | refresh_bloomfilter (pr->public_data.type, pr); | ||
465 | } | ||
466 | else | ||
467 | { | ||
468 | GNUNET_break (GNUNET_OK == | ||
469 | GNUNET_BLOCK_group_set_seen (pr->bg, | ||
470 | replies_seen, | ||
471 | pr->replies_seen_count)); | ||
472 | } | ||
473 | } | ||
474 | if (NULL != pr->gh) | ||
475 | GNUNET_DHT_get_filter_known_results (pr->gh, | ||
476 | replies_seen_count, | ||
477 | replies_seen); | ||
478 | } | ||
479 | |||
480 | |||
481 | /** | ||
482 | * Generate the message corresponding to the given pending request for | ||
483 | * transmission to other peers. | ||
484 | * | ||
485 | * @param pr request to generate the message for | ||
486 | * @return envelope with the request message | ||
487 | */ | ||
488 | struct GNUNET_MQ_Envelope * | ||
489 | GSF_pending_request_get_message_ (struct GSF_PendingRequest *pr) | ||
490 | { | ||
491 | struct GNUNET_MQ_Envelope *env; | ||
492 | struct GetMessage *gm; | ||
493 | struct GNUNET_PeerIdentity *ext; | ||
494 | unsigned int k; | ||
495 | uint32_t bm; | ||
496 | uint32_t prio; | ||
497 | size_t bf_size; | ||
498 | struct GNUNET_TIME_Absolute now; | ||
499 | int64_t ttl; | ||
500 | int do_route; | ||
501 | void *bf_data; | ||
502 | |||
503 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
504 | "Building request message for `%s' of type %d\n", | ||
505 | GNUNET_h2s (&pr->public_data.query), | ||
506 | pr->public_data.type); | ||
507 | k = 0; | ||
508 | bm = 0; | ||
509 | do_route = (0 == (pr->public_data.options & GSF_PRO_FORWARD_ONLY)); | ||
510 | if ((! do_route) && (pr->sender_pid == 0)) | ||
511 | { | ||
512 | GNUNET_break (0); | ||
513 | do_route = GNUNET_YES; | ||
514 | } | ||
515 | if (! do_route) | ||
516 | { | ||
517 | bm |= GET_MESSAGE_BIT_RETURN_TO; | ||
518 | k++; | ||
519 | } | ||
520 | if (NULL != pr->public_data.target) | ||
521 | { | ||
522 | bm |= GET_MESSAGE_BIT_TRANSMIT_TO; | ||
523 | k++; | ||
524 | } | ||
525 | if (GNUNET_OK != | ||
526 | GNUNET_BLOCK_group_serialize (pr->bg, | ||
527 | &bf_data, | ||
528 | &bf_size)) | ||
529 | { | ||
530 | bf_size = 0; | ||
531 | bf_data = NULL; | ||
532 | } | ||
533 | env = GNUNET_MQ_msg_extra (gm, | ||
534 | bf_size + k * sizeof(struct GNUNET_PeerIdentity), | ||
535 | GNUNET_MESSAGE_TYPE_FS_GET); | ||
536 | gm->type = htonl (pr->public_data.type); | ||
537 | if (do_route) | ||
538 | prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
539 | pr->public_data.priority + 1); | ||
540 | else | ||
541 | prio = 0; | ||
542 | pr->public_data.priority -= prio; | ||
543 | pr->public_data.num_transmissions++; | ||
544 | pr->public_data.respect_offered += prio; | ||
545 | gm->priority = htonl (prio); | ||
546 | now = GNUNET_TIME_absolute_get (); | ||
547 | ttl = (int64_t) (pr->public_data.ttl.abs_value_us - now.abs_value_us); | ||
548 | gm->ttl = htonl (ttl / 1000LL / 1000LL); | ||
549 | gm->reserved = htonl (0); | ||
550 | gm->hash_bitmap = htonl (bm); | ||
551 | gm->query = pr->public_data.query; | ||
552 | ext = (struct GNUNET_PeerIdentity *) &gm[1]; | ||
553 | k = 0; | ||
554 | if (! do_route) | ||
555 | GNUNET_PEER_resolve (pr->sender_pid, &ext[k++]); | ||
556 | if (NULL != pr->public_data.target) | ||
557 | ext[k++] = *pr->public_data.target; | ||
558 | GNUNET_memcpy (&ext[k], bf_data, bf_size); | ||
559 | GNUNET_free (bf_data); | ||
560 | return env; | ||
561 | } | ||
562 | |||
563 | |||
564 | /** | ||
565 | * Iterator to free pending requests. | ||
566 | * | ||
567 | * @param cls closure, unused | ||
568 | * @param key current key code | ||
569 | * @param value value in the hash map (pending request) | ||
570 | * @return #GNUNET_YES (we should continue to iterate) | ||
571 | */ | ||
572 | static int | ||
573 | clean_request (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
574 | { | ||
575 | struct GSF_PendingRequest *pr = value; | ||
576 | GSF_LocalLookupContinuation cont; | ||
577 | |||
578 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
579 | "Cleaning up pending request for `%s'.\n", | ||
580 | GNUNET_h2s (key)); | ||
581 | if (NULL != pr->cadet_request) | ||
582 | { | ||
583 | pr->cadet_retry_count = CADET_RETRY_MAX; | ||
584 | GSF_cadet_query_cancel (pr->cadet_request); | ||
585 | pr->cadet_request = NULL; | ||
586 | } | ||
587 | if (NULL != (cont = pr->llc_cont)) | ||
588 | { | ||
589 | pr->llc_cont = NULL; | ||
590 | cont (pr->llc_cont_cls, | ||
591 | pr, | ||
592 | pr->local_result); | ||
593 | } | ||
594 | GSF_plan_notify_request_done_ (pr); | ||
595 | GNUNET_free (pr->replies_seen); | ||
596 | GNUNET_BLOCK_group_destroy (pr->bg); | ||
597 | pr->bg = NULL; | ||
598 | GNUNET_PEER_change_rc (pr->sender_pid, -1); | ||
599 | pr->sender_pid = 0; | ||
600 | GNUNET_PEER_change_rc (pr->origin_pid, -1); | ||
601 | pr->origin_pid = 0; | ||
602 | if (NULL != pr->hnode) | ||
603 | { | ||
604 | GNUNET_CONTAINER_heap_remove_node (pr->hnode); | ||
605 | pr->hnode = NULL; | ||
606 | } | ||
607 | if (NULL != pr->qe) | ||
608 | { | ||
609 | GNUNET_DATASTORE_cancel (pr->qe); | ||
610 | pr->qe = NULL; | ||
611 | } | ||
612 | if (NULL != pr->gh) | ||
613 | { | ||
614 | GNUNET_DHT_get_stop (pr->gh); | ||
615 | pr->gh = NULL; | ||
616 | } | ||
617 | if (NULL != pr->warn_task) | ||
618 | { | ||
619 | GNUNET_SCHEDULER_cancel (pr->warn_task); | ||
620 | pr->warn_task = NULL; | ||
621 | } | ||
622 | GNUNET_assert ( | ||
623 | GNUNET_OK == | ||
624 | GNUNET_CONTAINER_multihashmap_remove (pr_map, &pr->public_data.query, pr)); | ||
625 | GNUNET_STATISTICS_update (GSF_stats, | ||
626 | gettext_noop ("# Pending requests active"), | ||
627 | -1, | ||
628 | GNUNET_NO); | ||
629 | GNUNET_free (pr); | ||
630 | return GNUNET_YES; | ||
631 | } | ||
632 | |||
633 | |||
634 | /** | ||
635 | * Explicitly cancel a pending request. | ||
636 | * | ||
637 | * @param pr request to cancel | ||
638 | * @param full_cleanup fully purge the request | ||
639 | */ | ||
640 | void | ||
641 | GSF_pending_request_cancel_ (struct GSF_PendingRequest *pr, int full_cleanup) | ||
642 | { | ||
643 | GSF_LocalLookupContinuation cont; | ||
644 | |||
645 | if (NULL == pr_map) | ||
646 | return; /* already cleaned up! */ | ||
647 | if (GNUNET_NO == full_cleanup) | ||
648 | { | ||
649 | /* make request inactive (we're no longer interested in more results), | ||
650 | * but do NOT remove from our data-structures, we still need it there | ||
651 | * to prevent the request from looping */ | ||
652 | pr->rh = NULL; | ||
653 | if (NULL != pr->cadet_request) | ||
654 | { | ||
655 | pr->cadet_retry_count = CADET_RETRY_MAX; | ||
656 | GSF_cadet_query_cancel (pr->cadet_request); | ||
657 | pr->cadet_request = NULL; | ||
658 | } | ||
659 | if (NULL != (cont = pr->llc_cont)) | ||
660 | { | ||
661 | pr->llc_cont = NULL; | ||
662 | cont (pr->llc_cont_cls, | ||
663 | pr, | ||
664 | pr->local_result); | ||
665 | } | ||
666 | GSF_plan_notify_request_done_ (pr); | ||
667 | if (NULL != pr->qe) | ||
668 | { | ||
669 | GNUNET_DATASTORE_cancel (pr->qe); | ||
670 | pr->qe = NULL; | ||
671 | } | ||
672 | if (NULL != pr->gh) | ||
673 | { | ||
674 | GNUNET_DHT_get_stop (pr->gh); | ||
675 | pr->gh = NULL; | ||
676 | } | ||
677 | if (NULL != pr->warn_task) | ||
678 | { | ||
679 | GNUNET_SCHEDULER_cancel (pr->warn_task); | ||
680 | pr->warn_task = NULL; | ||
681 | } | ||
682 | return; | ||
683 | } | ||
684 | GNUNET_assert (GNUNET_YES == | ||
685 | clean_request (NULL, &pr->public_data.query, pr)); | ||
686 | } | ||
687 | |||
688 | |||
689 | void | ||
690 | GSF_iterate_pending_requests_ (GSF_PendingRequestIterator it, void *cls) | ||
691 | { | ||
692 | GNUNET_CONTAINER_multihashmap_iterate ( | ||
693 | pr_map, | ||
694 | (GNUNET_CONTAINER_MultiHashMapIteratorCallback) it, | ||
695 | cls); | ||
696 | } | ||
697 | |||
698 | |||
699 | /** | ||
700 | * Closure for process_reply() function. | ||
701 | */ | ||
702 | struct ProcessReplyClosure | ||
703 | { | ||
704 | /** | ||
705 | * The data for the reply. | ||
706 | */ | ||
707 | const void *data; | ||
708 | |||
709 | /** | ||
710 | * Who gave us this reply? NULL for local host (or DHT) | ||
711 | */ | ||
712 | struct GSF_ConnectedPeer *sender; | ||
713 | |||
714 | /** | ||
715 | * When the reply expires. | ||
716 | */ | ||
717 | struct GNUNET_TIME_Absolute expiration; | ||
718 | |||
719 | /** | ||
720 | * Size of data. | ||
721 | */ | ||
722 | size_t size; | ||
723 | |||
724 | /** | ||
725 | * Type of the block. | ||
726 | */ | ||
727 | enum GNUNET_BLOCK_Type type; | ||
728 | |||
729 | /** | ||
730 | * How much was this reply worth to us? | ||
731 | */ | ||
732 | uint32_t priority; | ||
733 | |||
734 | /** | ||
735 | * Anonymity requirements for this reply. | ||
736 | */ | ||
737 | uint32_t anonymity_level; | ||
738 | |||
739 | /** | ||
740 | * Evaluation result (returned). | ||
741 | */ | ||
742 | enum GNUNET_BLOCK_ReplyEvaluationResult eval; | ||
743 | |||
744 | /** | ||
745 | * Did we find a matching request? | ||
746 | */ | ||
747 | int request_found; | ||
748 | }; | ||
749 | |||
750 | |||
751 | /** | ||
752 | * Update the performance data for the sender (if any) since | ||
753 | * the sender successfully answered one of our queries. | ||
754 | * | ||
755 | * @param prq information about the sender | ||
756 | * @param pr request that was satisfied | ||
757 | */ | ||
758 | static void | ||
759 | update_request_performance_data (struct ProcessReplyClosure *prq, | ||
760 | struct GSF_PendingRequest *pr) | ||
761 | { | ||
762 | if (prq->sender == NULL) | ||
763 | return; | ||
764 | GSF_peer_update_performance_ (prq->sender, | ||
765 | pr->public_data.start_time, | ||
766 | prq->priority); | ||
767 | } | ||
768 | |||
769 | |||
770 | /** | ||
771 | * We have received a reply; handle it! | ||
772 | * | ||
773 | * @param cls response (a `struct ProcessReplyClosure`) | ||
774 | * @param key our query | ||
775 | * @param value value in the hash map (info about the query) | ||
776 | * @return #GNUNET_YES (we should continue to iterate) | ||
777 | */ | ||
778 | static enum GNUNET_GenericReturnValue | ||
779 | process_reply (void *cls, | ||
780 | const struct GNUNET_HashCode *key, | ||
781 | void *value) | ||
782 | { | ||
783 | struct ProcessReplyClosure *prq = cls; | ||
784 | struct GSF_PendingRequest *pr = value; | ||
785 | struct GNUNET_HashCode chash; | ||
786 | struct GNUNET_TIME_Absolute last_transmission; | ||
787 | |||
788 | if (NULL == pr->rh) | ||
789 | return GNUNET_YES; | ||
790 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
791 | "Matched result (type %u) for query `%s' with pending request\n", | ||
792 | (unsigned int) prq->type, | ||
793 | GNUNET_h2s (key)); | ||
794 | GNUNET_STATISTICS_update (GSF_stats, | ||
795 | gettext_noop ("# replies received and matched"), | ||
796 | 1, | ||
797 | GNUNET_NO); | ||
798 | prq->eval = GNUNET_BLOCK_check_reply (GSF_block_ctx, | ||
799 | prq->type, | ||
800 | pr->bg, | ||
801 | key, | ||
802 | NULL, 0, | ||
803 | prq->data, | ||
804 | prq->size); | ||
805 | switch (prq->eval) | ||
806 | { | ||
807 | case GNUNET_BLOCK_REPLY_OK_MORE: | ||
808 | update_request_performance_data (prq, pr); | ||
809 | break; | ||
810 | case GNUNET_BLOCK_REPLY_OK_LAST: | ||
811 | /* short cut: stop processing early, no BF-update, etc. */ | ||
812 | update_request_performance_data (prq, pr); | ||
813 | GNUNET_LOAD_update (GSF_rt_entry_lifetime, | ||
814 | GNUNET_TIME_absolute_get_duration ( | ||
815 | pr->public_data.start_time) | ||
816 | .rel_value_us); | ||
817 | if (GNUNET_YES != | ||
818 | GSF_request_plan_reference_get_last_transmission_ (pr->public_data | ||
819 | .pr_head, | ||
820 | prq->sender, | ||
821 | &last_transmission)) | ||
822 | last_transmission = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
823 | /* pass on to other peers / local clients */ | ||
824 | pr->rh (pr->rh_cls, | ||
825 | prq->eval, | ||
826 | pr, | ||
827 | prq->anonymity_level, | ||
828 | prq->expiration, | ||
829 | last_transmission, | ||
830 | prq->type, | ||
831 | prq->data, | ||
832 | prq->size); | ||
833 | return GNUNET_YES; | ||
834 | case GNUNET_BLOCK_REPLY_OK_DUPLICATE: | ||
835 | #if INSANE_STATISTICS | ||
836 | GNUNET_STATISTICS_update (GSF_stats, | ||
837 | "# duplicate replies discarded (bloomfilter)", | ||
838 | 1, | ||
839 | GNUNET_NO); | ||
840 | #endif | ||
841 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
842 | "Duplicate response, discarding.\n"); | ||
843 | return GNUNET_YES; /* duplicate */ | ||
844 | case GNUNET_BLOCK_REPLY_IRRELEVANT: | ||
845 | GNUNET_STATISTICS_update (GSF_stats, | ||
846 | "# irrelevant replies discarded", | ||
847 | 1, | ||
848 | GNUNET_NO); | ||
849 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
850 | "Irrelevant response, ignoring.\n"); | ||
851 | return GNUNET_YES; | ||
852 | case GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED: | ||
853 | GNUNET_break (0); /* bad installation? */ | ||
854 | return GNUNET_NO; | ||
855 | } | ||
856 | /* update bloomfilter */ | ||
857 | GNUNET_CRYPTO_hash (prq->data, | ||
858 | prq->size, | ||
859 | &chash); | ||
860 | GSF_pending_request_update_ (pr, | ||
861 | &chash, | ||
862 | 1); | ||
863 | if (NULL == prq->sender) | ||
864 | { | ||
865 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
866 | "Found result for query `%s' in local datastore\n", | ||
867 | GNUNET_h2s (key)); | ||
868 | GNUNET_STATISTICS_update (GSF_stats, | ||
869 | gettext_noop ("# results found locally"), | ||
870 | 1, | ||
871 | GNUNET_NO); | ||
872 | } | ||
873 | else | ||
874 | { | ||
875 | GSF_dht_lookup_ (pr); | ||
876 | } | ||
877 | prq->priority += pr->public_data.original_priority; | ||
878 | pr->public_data.priority = 0; | ||
879 | pr->public_data.original_priority = 0; | ||
880 | pr->public_data.results_found++; | ||
881 | prq->request_found = GNUNET_YES; | ||
882 | /* finally, pass on to other peer / local client */ | ||
883 | if (! GSF_request_plan_reference_get_last_transmission_ (pr->public_data | ||
884 | .pr_head, | ||
885 | prq->sender, | ||
886 | &last_transmission)) | ||
887 | last_transmission = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
888 | pr->rh (pr->rh_cls, | ||
889 | prq->eval, | ||
890 | pr, | ||
891 | prq->anonymity_level, | ||
892 | prq->expiration, | ||
893 | last_transmission, | ||
894 | prq->type, | ||
895 | prq->data, | ||
896 | prq->size); | ||
897 | return GNUNET_YES; | ||
898 | } | ||
899 | |||
900 | |||
901 | /** | ||
902 | * Context for put_migration_continuation(). | ||
903 | */ | ||
904 | struct PutMigrationContext | ||
905 | { | ||
906 | /** | ||
907 | * Start time for the operation. | ||
908 | */ | ||
909 | struct GNUNET_TIME_Absolute start; | ||
910 | |||
911 | /** | ||
912 | * Request origin. | ||
913 | */ | ||
914 | struct GNUNET_PeerIdentity origin; | ||
915 | |||
916 | /** | ||
917 | * #GNUNET_YES if we had a matching request for this block, | ||
918 | * #GNUNET_NO if not. | ||
919 | */ | ||
920 | int requested; | ||
921 | }; | ||
922 | |||
923 | |||
924 | /** | ||
925 | * Continuation called to notify client about result of the | ||
926 | * operation. | ||
927 | * | ||
928 | * @param cls closure | ||
929 | * @param success #GNUNET_SYSERR on failure | ||
930 | * @param min_expiration minimum expiration time required for content to be stored | ||
931 | * @param msg NULL on success, otherwise an error message | ||
932 | */ | ||
933 | static void | ||
934 | put_migration_continuation (void *cls, | ||
935 | int success, | ||
936 | struct GNUNET_TIME_Absolute min_expiration, | ||
937 | const char *msg) | ||
938 | { | ||
939 | struct PutMigrationContext *pmc = cls; | ||
940 | struct GSF_ConnectedPeer *cp; | ||
941 | struct GNUNET_TIME_Relative mig_pause; | ||
942 | struct GSF_PeerPerformanceData *ppd; | ||
943 | |||
944 | if (NULL != datastore_put_load) | ||
945 | { | ||
946 | if (GNUNET_SYSERR != success) | ||
947 | { | ||
948 | GNUNET_LOAD_update (datastore_put_load, | ||
949 | GNUNET_TIME_absolute_get_duration (pmc->start) | ||
950 | .rel_value_us); | ||
951 | } | ||
952 | else | ||
953 | { | ||
954 | /* on queue failure / timeout, increase the put load dramatically */ | ||
955 | GNUNET_LOAD_update (datastore_put_load, | ||
956 | GNUNET_TIME_UNIT_MINUTES.rel_value_us); | ||
957 | } | ||
958 | } | ||
959 | cp = GSF_peer_get_ (&pmc->origin); | ||
960 | if (GNUNET_OK == success) | ||
961 | { | ||
962 | if (NULL != cp) | ||
963 | { | ||
964 | ppd = GSF_get_peer_performance_data_ (cp); | ||
965 | ppd->migration_delay.rel_value_us /= 2; | ||
966 | } | ||
967 | GNUNET_free (pmc); | ||
968 | return; | ||
969 | } | ||
970 | if ((GNUNET_NO == success) && (GNUNET_NO == pmc->requested) && (NULL != cp)) | ||
971 | { | ||
972 | ppd = GSF_get_peer_performance_data_ (cp); | ||
973 | if (min_expiration.abs_value_us > 0) | ||
974 | { | ||
975 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
976 | "Asking to stop migration for %s because datastore is full\n", | ||
977 | GNUNET_STRINGS_relative_time_to_string ( | ||
978 | GNUNET_TIME_absolute_get_remaining (min_expiration), | ||
979 | GNUNET_YES)); | ||
980 | GSF_block_peer_migration_ (cp, min_expiration); | ||
981 | } | ||
982 | else | ||
983 | { | ||
984 | ppd->migration_delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_SECONDS, | ||
985 | ppd->migration_delay); | ||
986 | ppd->migration_delay = | ||
987 | GNUNET_TIME_relative_min (GNUNET_TIME_UNIT_HOURS, ppd->migration_delay); | ||
988 | mig_pause.rel_value_us = | ||
989 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
990 | ppd->migration_delay.rel_value_us); | ||
991 | ppd->migration_delay = | ||
992 | GNUNET_TIME_relative_saturating_multiply (ppd->migration_delay, 2); | ||
993 | GNUNET_log ( | ||
994 | GNUNET_ERROR_TYPE_DEBUG, | ||
995 | "Replicated content already exists locally, asking to stop migration for %s\n", | ||
996 | GNUNET_STRINGS_relative_time_to_string (mig_pause, GNUNET_YES)); | ||
997 | GSF_block_peer_migration_ (cp, | ||
998 | GNUNET_TIME_relative_to_absolute (mig_pause)); | ||
999 | } | ||
1000 | } | ||
1001 | GNUNET_free (pmc); | ||
1002 | GNUNET_STATISTICS_update (GSF_stats, | ||
1003 | gettext_noop ("# Datastore `PUT' failures"), | ||
1004 | 1, | ||
1005 | GNUNET_NO); | ||
1006 | } | ||
1007 | |||
1008 | |||
1009 | /** | ||
1010 | * Test if the DATABASE (PUT) load on this peer is too high | ||
1011 | * to even consider processing the query at | ||
1012 | * all. | ||
1013 | * | ||
1014 | * @param priority the priority of the item | ||
1015 | * @return #GNUNET_YES if the load is too high to do anything (load high) | ||
1016 | * #GNUNET_NO to process normally (load normal or low) | ||
1017 | */ | ||
1018 | static int | ||
1019 | test_put_load_too_high (uint32_t priority) | ||
1020 | { | ||
1021 | double ld; | ||
1022 | |||
1023 | if (NULL == datastore_put_load) | ||
1024 | return GNUNET_NO; | ||
1025 | if (GNUNET_LOAD_get_average (datastore_put_load) < 50) | ||
1026 | return GNUNET_NO; /* very fast */ | ||
1027 | ld = GNUNET_LOAD_get_load (datastore_put_load); | ||
1028 | if (ld < 2.0 * (1 + priority)) | ||
1029 | return GNUNET_NO; | ||
1030 | GNUNET_STATISTICS_update (GSF_stats, | ||
1031 | gettext_noop ( | ||
1032 | "# storage requests dropped due to high load"), | ||
1033 | 1, | ||
1034 | GNUNET_NO); | ||
1035 | return GNUNET_YES; | ||
1036 | } | ||
1037 | |||
1038 | |||
1039 | /** | ||
1040 | * Iterator called on each result obtained for a DHT | ||
1041 | * operation that expects a reply | ||
1042 | * | ||
1043 | * @param cls closure | ||
1044 | * @param exp when will this value expire | ||
1045 | * @param key key of the result | ||
1046 | * @param trunc_peer truncated peer, NULL for none | ||
1047 | * @param get_path peers on reply path (or NULL if not recorded) | ||
1048 | * @param get_path_length number of entries in @a get_path | ||
1049 | * @param put_path peers on the PUT path (or NULL if not recorded) | ||
1050 | * @param put_path_length number of entries in @a get_path | ||
1051 | * @param type type of the result | ||
1052 | * @param size number of bytes in @a data | ||
1053 | * @param data pointer to the result data | ||
1054 | */ | ||
1055 | static void | ||
1056 | handle_dht_reply (void *cls, | ||
1057 | struct GNUNET_TIME_Absolute exp, | ||
1058 | const struct GNUNET_HashCode *key, | ||
1059 | const struct GNUNET_PeerIdentity *trunc_peer, | ||
1060 | const struct GNUNET_DHT_PathElement *get_path, | ||
1061 | unsigned int get_path_length, | ||
1062 | const struct GNUNET_DHT_PathElement *put_path, | ||
1063 | unsigned int put_path_length, | ||
1064 | enum GNUNET_BLOCK_Type type, | ||
1065 | size_t size, | ||
1066 | const void *data) | ||
1067 | { | ||
1068 | struct GSF_PendingRequest *pr = cls; | ||
1069 | struct ProcessReplyClosure prq; | ||
1070 | struct PutMigrationContext *pmc; | ||
1071 | |||
1072 | GNUNET_STATISTICS_update (GSF_stats, | ||
1073 | gettext_noop ("# Replies received from DHT"), | ||
1074 | 1, | ||
1075 | GNUNET_NO); | ||
1076 | memset (&prq, 0, sizeof(prq)); | ||
1077 | prq.data = data; | ||
1078 | prq.expiration = exp; | ||
1079 | /* do not allow migrated content to live longer than 1 year */ | ||
1080 | prq.expiration = GNUNET_TIME_absolute_min (GNUNET_TIME_relative_to_absolute ( | ||
1081 | GNUNET_TIME_UNIT_YEARS), | ||
1082 | prq.expiration); | ||
1083 | prq.size = size; | ||
1084 | prq.type = type; | ||
1085 | process_reply (&prq, | ||
1086 | key, | ||
1087 | pr); | ||
1088 | if ((GNUNET_YES == active_to_migration) && | ||
1089 | (GNUNET_NO == test_put_load_too_high (prq.priority))) | ||
1090 | { | ||
1091 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1092 | "Replicating result for query `%s' with priority %u\n", | ||
1093 | GNUNET_h2s (key), | ||
1094 | prq.priority); | ||
1095 | pmc = GNUNET_new (struct PutMigrationContext); | ||
1096 | pmc->start = GNUNET_TIME_absolute_get (); | ||
1097 | pmc->requested = GNUNET_YES; | ||
1098 | if (NULL == GNUNET_DATASTORE_put (GSF_dsh, | ||
1099 | 0, | ||
1100 | key, | ||
1101 | size, | ||
1102 | data, | ||
1103 | type, | ||
1104 | prq.priority, | ||
1105 | 1 /* anonymity */, | ||
1106 | 0 /* replication */, | ||
1107 | exp, | ||
1108 | 1 + prq.priority, | ||
1109 | MAX_DATASTORE_QUEUE, | ||
1110 | &put_migration_continuation, | ||
1111 | pmc)) | ||
1112 | { | ||
1113 | put_migration_continuation (pmc, | ||
1114 | GNUNET_SYSERR, | ||
1115 | GNUNET_TIME_UNIT_ZERO_ABS, | ||
1116 | NULL); | ||
1117 | } | ||
1118 | } | ||
1119 | } | ||
1120 | |||
1121 | |||
1122 | /** | ||
1123 | * Consider looking up the data in the DHT (anonymity-level permitting). | ||
1124 | * | ||
1125 | * @param pr the pending request to process | ||
1126 | */ | ||
1127 | void | ||
1128 | GSF_dht_lookup_ (struct GSF_PendingRequest *pr) | ||
1129 | { | ||
1130 | const void *xquery; | ||
1131 | size_t xquery_size; | ||
1132 | struct GNUNET_PeerIdentity pi; | ||
1133 | char buf[sizeof(struct GNUNET_HashCode) * 2] GNUNET_ALIGN; | ||
1134 | |||
1135 | if (0 != pr->public_data.anonymity_level) | ||
1136 | return; | ||
1137 | if (NULL != pr->gh) | ||
1138 | { | ||
1139 | GNUNET_DHT_get_stop (pr->gh); | ||
1140 | pr->gh = NULL; | ||
1141 | } | ||
1142 | xquery = NULL; | ||
1143 | xquery_size = 0; | ||
1144 | if (0 != (pr->public_data.options & GSF_PRO_FORWARD_ONLY)) | ||
1145 | { | ||
1146 | GNUNET_assert (0 != pr->sender_pid); | ||
1147 | GNUNET_PEER_resolve (pr->sender_pid, &pi); | ||
1148 | GNUNET_memcpy (&buf[xquery_size], &pi, sizeof(struct GNUNET_PeerIdentity)); | ||
1149 | xquery_size += sizeof(struct GNUNET_PeerIdentity); | ||
1150 | } | ||
1151 | pr->gh = GNUNET_DHT_get_start (GSF_dht, | ||
1152 | pr->public_data.type, | ||
1153 | &pr->public_data.query, | ||
1154 | DHT_GET_REPLICATION, | ||
1155 | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, | ||
1156 | xquery, | ||
1157 | xquery_size, | ||
1158 | &handle_dht_reply, | ||
1159 | pr); | ||
1160 | if ((NULL != pr->gh) && (0 != pr->replies_seen_count)) | ||
1161 | GNUNET_DHT_get_filter_known_results (pr->gh, | ||
1162 | pr->replies_seen_count, | ||
1163 | pr->replies_seen); | ||
1164 | } | ||
1165 | |||
1166 | |||
1167 | /** | ||
1168 | * Function called with a reply from the cadet. | ||
1169 | * | ||
1170 | * @param cls the pending request struct | ||
1171 | * @param type type of the block, ANY on error | ||
1172 | * @param expiration expiration time for the block | ||
1173 | * @param data_size number of bytes in @a data, 0 on error | ||
1174 | * @param data reply block data, NULL on error | ||
1175 | */ | ||
1176 | static void | ||
1177 | cadet_reply_proc (void *cls, | ||
1178 | enum GNUNET_BLOCK_Type type, | ||
1179 | struct GNUNET_TIME_Absolute expiration, | ||
1180 | size_t data_size, | ||
1181 | const void *data) | ||
1182 | { | ||
1183 | struct GSF_PendingRequest *pr = cls; | ||
1184 | struct ProcessReplyClosure prq; | ||
1185 | struct GNUNET_HashCode query; | ||
1186 | |||
1187 | pr->cadet_request = NULL; | ||
1188 | if (GNUNET_OK != | ||
1189 | GNUNET_BLOCK_check_block (GSF_block_ctx, | ||
1190 | type, | ||
1191 | data, | ||
1192 | data_size)) | ||
1193 | { | ||
1194 | GNUNET_break_op (0); | ||
1195 | return; | ||
1196 | } | ||
1197 | if (GNUNET_BLOCK_TYPE_ANY == type) | ||
1198 | { | ||
1199 | GNUNET_break (NULL == data); | ||
1200 | GNUNET_break (0 == data_size); | ||
1201 | pr->cadet_retry_count++; | ||
1202 | if (pr->cadet_retry_count >= CADET_RETRY_MAX) | ||
1203 | return; /* give up on cadet */ | ||
1204 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error retrieiving block via cadet\n"); | ||
1205 | /* retry -- without delay, as this is non-anonymous | ||
1206 | and cadet/cadet connect will take some time anyway */ | ||
1207 | pr->cadet_request = GSF_cadet_query (pr->public_data.target, | ||
1208 | &pr->public_data.query, | ||
1209 | pr->public_data.type, | ||
1210 | &cadet_reply_proc, | ||
1211 | pr); | ||
1212 | return; | ||
1213 | } | ||
1214 | if (GNUNET_YES != | ||
1215 | GNUNET_BLOCK_get_key (GSF_block_ctx, | ||
1216 | type, | ||
1217 | data, | ||
1218 | data_size, | ||
1219 | &query)) | ||
1220 | { | ||
1221 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1222 | "Failed to derive key for block of type %d\n", | ||
1223 | (int) type); | ||
1224 | GNUNET_break_op (0); | ||
1225 | return; | ||
1226 | } | ||
1227 | GNUNET_STATISTICS_update (GSF_stats, | ||
1228 | gettext_noop ("# Replies received from CADET"), | ||
1229 | 1, | ||
1230 | GNUNET_NO); | ||
1231 | memset (&prq, 0, sizeof(prq)); | ||
1232 | prq.data = data; | ||
1233 | prq.expiration = expiration; | ||
1234 | /* do not allow migrated content to live longer than 1 year */ | ||
1235 | prq.expiration = GNUNET_TIME_absolute_min (GNUNET_TIME_relative_to_absolute ( | ||
1236 | GNUNET_TIME_UNIT_YEARS), | ||
1237 | prq.expiration); | ||
1238 | prq.size = data_size; | ||
1239 | prq.type = type; | ||
1240 | process_reply (&prq, | ||
1241 | &query, | ||
1242 | pr); | ||
1243 | } | ||
1244 | |||
1245 | |||
1246 | /** | ||
1247 | * Consider downloading via cadet (if possible) | ||
1248 | * | ||
1249 | * @param pr the pending request to process | ||
1250 | */ | ||
1251 | void | ||
1252 | GSF_cadet_lookup_ (struct GSF_PendingRequest *pr) | ||
1253 | { | ||
1254 | if (0 != pr->public_data.anonymity_level) | ||
1255 | return; | ||
1256 | if (0 == pr->public_data.target) | ||
1257 | { | ||
1258 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1259 | "Cannot do cadet-based download, target peer not known\n"); | ||
1260 | return; | ||
1261 | } | ||
1262 | if (NULL != pr->cadet_request) | ||
1263 | return; | ||
1264 | pr->cadet_request = GSF_cadet_query (pr->public_data.target, | ||
1265 | &pr->public_data.query, | ||
1266 | pr->public_data.type, | ||
1267 | &cadet_reply_proc, | ||
1268 | pr); | ||
1269 | } | ||
1270 | |||
1271 | |||
1272 | /** | ||
1273 | * Task that issues a warning if the datastore lookup takes too long. | ||
1274 | * | ||
1275 | * @param cls the `struct GSF_PendingRequest` | ||
1276 | */ | ||
1277 | static void | ||
1278 | warn_delay_task (void *cls) | ||
1279 | { | ||
1280 | struct GSF_PendingRequest *pr = cls; | ||
1281 | |||
1282 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, | ||
1283 | _ ("Datastore lookup already took %s!\n"), | ||
1284 | GNUNET_STRINGS_relative_time_to_string ( | ||
1285 | GNUNET_TIME_absolute_get_duration (pr->qe_start), | ||
1286 | GNUNET_YES)); | ||
1287 | pr->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | ||
1288 | &warn_delay_task, | ||
1289 | pr); | ||
1290 | } | ||
1291 | |||
1292 | |||
1293 | /** | ||
1294 | * Task that issues a warning if the datastore lookup takes too long. | ||
1295 | * | ||
1296 | * @param cls the `struct GSF_PendingRequest` | ||
1297 | */ | ||
1298 | static void | ||
1299 | odc_warn_delay_task (void *cls) | ||
1300 | { | ||
1301 | struct GSF_PendingRequest *pr = cls; | ||
1302 | |||
1303 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1304 | _ ("On-demand lookup already took %s!\n"), | ||
1305 | GNUNET_STRINGS_relative_time_to_string ( | ||
1306 | GNUNET_TIME_absolute_get_duration (pr->qe_start), | ||
1307 | GNUNET_YES)); | ||
1308 | pr->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | ||
1309 | &odc_warn_delay_task, | ||
1310 | pr); | ||
1311 | } | ||
1312 | |||
1313 | |||
1314 | /* Call our continuation (if we have any) */ | ||
1315 | static void | ||
1316 | call_continuation (struct GSF_PendingRequest *pr) | ||
1317 | { | ||
1318 | GSF_LocalLookupContinuation cont = pr->llc_cont; | ||
1319 | |||
1320 | GNUNET_assert (NULL == pr->qe); | ||
1321 | if (NULL != pr->warn_task) | ||
1322 | { | ||
1323 | GNUNET_SCHEDULER_cancel (pr->warn_task); | ||
1324 | pr->warn_task = NULL; | ||
1325 | } | ||
1326 | if (NULL == cont) | ||
1327 | return; /* no continuation */ | ||
1328 | pr->llc_cont = NULL; | ||
1329 | if (0 != (GSF_PRO_LOCAL_ONLY & pr->public_data.options)) | ||
1330 | { | ||
1331 | if (GNUNET_BLOCK_REPLY_OK_LAST != pr->local_result) | ||
1332 | { | ||
1333 | /* Signal that we are done and that there won't be any | ||
1334 | additional results to allow client to clean up state. */ | ||
1335 | pr->rh (pr->rh_cls, | ||
1336 | GNUNET_BLOCK_REPLY_OK_LAST, | ||
1337 | pr, | ||
1338 | UINT32_MAX, | ||
1339 | GNUNET_TIME_UNIT_ZERO_ABS, | ||
1340 | GNUNET_TIME_UNIT_FOREVER_ABS, | ||
1341 | GNUNET_BLOCK_TYPE_ANY, | ||
1342 | NULL, | ||
1343 | 0); | ||
1344 | } | ||
1345 | /* Finally, call our continuation to signal that we are | ||
1346 | done with local processing of this request; i.e. to | ||
1347 | start reading again from the client. */ | ||
1348 | cont (pr->llc_cont_cls, | ||
1349 | NULL, | ||
1350 | GNUNET_BLOCK_REPLY_OK_LAST); | ||
1351 | return; | ||
1352 | } | ||
1353 | |||
1354 | cont (pr->llc_cont_cls, | ||
1355 | pr, | ||
1356 | pr->local_result); | ||
1357 | } | ||
1358 | |||
1359 | |||
1360 | /* Update stats and call continuation */ | ||
1361 | static void | ||
1362 | no_more_local_results (struct GSF_PendingRequest *pr) | ||
1363 | { | ||
1364 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, | ||
1365 | "No further local responses available.\n"); | ||
1366 | #if INSANE_STATISTICS | ||
1367 | if ((GNUNET_BLOCK_TYPE_FS_DBLOCK == pr->public_data.type) || | ||
1368 | (GNUNET_BLOCK_TYPE_FS_IBLOCK == pr->public_data.type)) | ||
1369 | GNUNET_STATISTICS_update (GSF_stats, | ||
1370 | gettext_noop ( | ||
1371 | "# requested DBLOCK or IBLOCK not found"), | ||
1372 | 1, | ||
1373 | GNUNET_NO); | ||
1374 | #endif | ||
1375 | call_continuation (pr); | ||
1376 | } | ||
1377 | |||
1378 | |||
1379 | /* forward declaration */ | ||
1380 | static void | ||
1381 | process_local_reply (void *cls, | ||
1382 | const struct GNUNET_HashCode *key, | ||
1383 | size_t size, | ||
1384 | const void *data, | ||
1385 | enum GNUNET_BLOCK_Type type, | ||
1386 | uint32_t priority, | ||
1387 | uint32_t anonymity, | ||
1388 | uint32_t replication, | ||
1389 | struct GNUNET_TIME_Absolute expiration, | ||
1390 | uint64_t uid); | ||
1391 | |||
1392 | |||
1393 | /* Start a local query */ | ||
1394 | static void | ||
1395 | start_local_query (struct GSF_PendingRequest *pr, | ||
1396 | uint64_t next_uid, | ||
1397 | bool random) | ||
1398 | { | ||
1399 | pr->qe_start = GNUNET_TIME_absolute_get (); | ||
1400 | pr->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | ||
1401 | &warn_delay_task, | ||
1402 | pr); | ||
1403 | pr->qe = GNUNET_DATASTORE_get_key (GSF_dsh, | ||
1404 | next_uid, | ||
1405 | random, | ||
1406 | &pr->public_data.query, | ||
1407 | pr->public_data.type == | ||
1408 | GNUNET_BLOCK_TYPE_FS_DBLOCK | ||
1409 | ? GNUNET_BLOCK_TYPE_ANY | ||
1410 | : pr->public_data.type, | ||
1411 | (0 != (GSF_PRO_PRIORITY_UNLIMITED | ||
1412 | & pr->public_data.options)) | ||
1413 | ? UINT_MAX | ||
1414 | : 1 | ||
1415 | /* queue priority */, | ||
1416 | (0 != (GSF_PRO_PRIORITY_UNLIMITED | ||
1417 | & pr->public_data.options)) | ||
1418 | ? UINT_MAX | ||
1419 | : GSF_datastore_queue_size | ||
1420 | /* max queue size */, | ||
1421 | &process_local_reply, | ||
1422 | pr); | ||
1423 | if (NULL != pr->qe) | ||
1424 | return; | ||
1425 | GNUNET_log ( | ||
1426 | GNUNET_ERROR_TYPE_DEBUG, | ||
1427 | "ERROR Requesting `%s' of type %d with next_uid %llu from datastore.\n", | ||
1428 | GNUNET_h2s (&pr->public_data.query), | ||
1429 | pr->public_data.type, | ||
1430 | (unsigned long long) next_uid); | ||
1431 | GNUNET_STATISTICS_update (GSF_stats, | ||
1432 | gettext_noop ( | ||
1433 | "# Datastore lookups concluded (error queueing)"), | ||
1434 | 1, | ||
1435 | GNUNET_NO); | ||
1436 | call_continuation (pr); | ||
1437 | } | ||
1438 | |||
1439 | |||
1440 | /** | ||
1441 | * We're processing (local) results for a search request | ||
1442 | * from another peer. Pass applicable results to the | ||
1443 | * peer and if we are done either clean up (operation | ||
1444 | * complete) or forward to other peers (more results possible). | ||
1445 | * | ||
1446 | * @param cls our closure (`struct GSF_PendingRequest *`) | ||
1447 | * @param key key for the content | ||
1448 | * @param size number of bytes in @a data | ||
1449 | * @param data content stored | ||
1450 | * @param type type of the content | ||
1451 | * @param priority priority of the content | ||
1452 | * @param anonymity anonymity-level for the content | ||
1453 | * @param replication replication-level for the content | ||
1454 | * @param expiration expiration time for the content | ||
1455 | * @param uid unique identifier for the datum; | ||
1456 | * maybe 0 if no unique identifier is available | ||
1457 | */ | ||
1458 | static void | ||
1459 | process_local_reply (void *cls, | ||
1460 | const struct GNUNET_HashCode *key, | ||
1461 | size_t size, | ||
1462 | const void *data, | ||
1463 | enum GNUNET_BLOCK_Type type, | ||
1464 | uint32_t priority, | ||
1465 | uint32_t anonymity, | ||
1466 | uint32_t replication, | ||
1467 | struct GNUNET_TIME_Absolute expiration, | ||
1468 | uint64_t uid) | ||
1469 | { | ||
1470 | struct GSF_PendingRequest *pr = cls; | ||
1471 | struct ProcessReplyClosure prq; | ||
1472 | struct GNUNET_HashCode query; | ||
1473 | unsigned int old_rf; | ||
1474 | |||
1475 | GNUNET_SCHEDULER_cancel (pr->warn_task); | ||
1476 | pr->warn_task = NULL; | ||
1477 | if (NULL == pr->qe) | ||
1478 | goto called_from_on_demand; | ||
1479 | pr->qe = NULL; | ||
1480 | if ( | ||
1481 | (NULL == key) && pr->seen_null && | ||
1482 | ! pr->have_first_uid) /* We have hit the end for the 2nd time with no results */ | ||
1483 | { | ||
1484 | /* No results */ | ||
1485 | #if INSANE_STATISTICS | ||
1486 | GNUNET_STATISTICS_update (GSF_stats, | ||
1487 | gettext_noop ( | ||
1488 | "# Datastore lookups concluded (no results)"), | ||
1489 | 1, | ||
1490 | GNUNET_NO); | ||
1491 | #endif | ||
1492 | no_more_local_results (pr); | ||
1493 | return; | ||
1494 | } | ||
1495 | if (((NULL == key) && | ||
1496 | pr->seen_null) || /* We have hit the end for the 2nd time OR */ | ||
1497 | (pr->seen_null && pr->have_first_uid && | ||
1498 | (uid >= pr->first_uid))) /* We have hit the end and past first UID */ | ||
1499 | { | ||
1500 | /* Seen all results */ | ||
1501 | GNUNET_STATISTICS_update (GSF_stats, | ||
1502 | gettext_noop ( | ||
1503 | "# Datastore lookups concluded (seen all)"), | ||
1504 | 1, | ||
1505 | GNUNET_NO); | ||
1506 | no_more_local_results (pr); | ||
1507 | return; | ||
1508 | } | ||
1509 | if (NULL == key) | ||
1510 | { | ||
1511 | GNUNET_assert (! pr->seen_null); | ||
1512 | pr->seen_null = true; | ||
1513 | start_local_query (pr, 0 /* next_uid */, false /* random */); | ||
1514 | return; | ||
1515 | } | ||
1516 | if (! pr->have_first_uid) | ||
1517 | { | ||
1518 | pr->first_uid = uid; | ||
1519 | pr->have_first_uid = true; | ||
1520 | } | ||
1521 | pr->result_count++; | ||
1522 | if (pr->result_count > MAX_RESULTS) | ||
1523 | { | ||
1524 | GNUNET_STATISTICS_update ( | ||
1525 | GSF_stats, | ||
1526 | gettext_noop ("# Datastore lookups aborted (more than MAX_RESULTS)"), | ||
1527 | 1, | ||
1528 | GNUNET_NO); | ||
1529 | no_more_local_results (pr); | ||
1530 | return; | ||
1531 | } | ||
1532 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1533 | "Received reply for `%s' of type %d with UID %llu from datastore.\n", | ||
1534 | GNUNET_h2s (key), | ||
1535 | type, | ||
1536 | (unsigned long long) uid); | ||
1537 | if (GNUNET_BLOCK_TYPE_FS_ONDEMAND == type) | ||
1538 | { | ||
1539 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1540 | "Found ONDEMAND block, performing on-demand encoding\n"); | ||
1541 | GNUNET_STATISTICS_update (GSF_stats, | ||
1542 | gettext_noop ( | ||
1543 | "# on-demand blocks matched requests"), | ||
1544 | 1, | ||
1545 | GNUNET_NO); | ||
1546 | pr->qe_start = GNUNET_TIME_absolute_get (); | ||
1547 | pr->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | ||
1548 | &odc_warn_delay_task, | ||
1549 | pr); | ||
1550 | if (GNUNET_OK == GNUNET_FS_handle_on_demand_block (key, | ||
1551 | size, | ||
1552 | data, | ||
1553 | type, | ||
1554 | priority, | ||
1555 | anonymity, | ||
1556 | replication, | ||
1557 | expiration, | ||
1558 | uid, | ||
1559 | &process_local_reply, | ||
1560 | pr)) | ||
1561 | { | ||
1562 | GNUNET_STATISTICS_update (GSF_stats, | ||
1563 | gettext_noop ( | ||
1564 | "# on-demand lookups performed successfully"), | ||
1565 | 1, | ||
1566 | GNUNET_NO); | ||
1567 | return; /* we're done */ | ||
1568 | } | ||
1569 | GNUNET_STATISTICS_update (GSF_stats, | ||
1570 | gettext_noop ("# on-demand lookups failed"), | ||
1571 | 1, | ||
1572 | GNUNET_NO); | ||
1573 | GNUNET_SCHEDULER_cancel (pr->warn_task); | ||
1574 | start_local_query (pr, uid + 1 /* next_uid */, false /* random */); | ||
1575 | return; | ||
1576 | } | ||
1577 | called_from_on_demand: | ||
1578 | old_rf = pr->public_data.results_found; | ||
1579 | memset (&prq, 0, sizeof(prq)); | ||
1580 | prq.data = data; | ||
1581 | prq.expiration = expiration; | ||
1582 | prq.size = size; | ||
1583 | if (GNUNET_OK != | ||
1584 | GNUNET_BLOCK_get_key (GSF_block_ctx, | ||
1585 | type, | ||
1586 | data, | ||
1587 | size, | ||
1588 | &query)) | ||
1589 | { | ||
1590 | GNUNET_break (0); | ||
1591 | GNUNET_DATASTORE_remove (GSF_dsh, | ||
1592 | key, | ||
1593 | size, | ||
1594 | data, | ||
1595 | UINT_MAX, | ||
1596 | UINT_MAX, | ||
1597 | NULL, | ||
1598 | NULL); | ||
1599 | start_local_query (pr, uid + 1 /* next_uid */, false /* random */); | ||
1600 | return; | ||
1601 | } | ||
1602 | prq.type = type; | ||
1603 | prq.priority = priority; | ||
1604 | prq.request_found = GNUNET_NO; | ||
1605 | prq.anonymity_level = anonymity; | ||
1606 | if ((0 == old_rf) && (0 == pr->public_data.results_found)) | ||
1607 | GSF_update_datastore_delay_ (pr->public_data.start_time); | ||
1608 | process_reply (&prq, | ||
1609 | key, | ||
1610 | pr); | ||
1611 | pr->local_result = prq.eval; | ||
1612 | if (GNUNET_BLOCK_REPLY_OK_LAST == prq.eval) | ||
1613 | { | ||
1614 | GNUNET_STATISTICS_update ( | ||
1615 | GSF_stats, | ||
1616 | gettext_noop ("# Datastore lookups concluded (found last result)"), | ||
1617 | 1, | ||
1618 | GNUNET_NO); | ||
1619 | call_continuation (pr); | ||
1620 | return; | ||
1621 | } | ||
1622 | if ((0 == (GSF_PRO_PRIORITY_UNLIMITED & pr->public_data.options)) && | ||
1623 | ((GNUNET_YES == GSF_test_get_load_too_high_ (0)) || | ||
1624 | (pr->public_data.results_found > 5 + 2 * pr->public_data.priority))) | ||
1625 | { | ||
1626 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Load too high, done with request\n"); | ||
1627 | GNUNET_STATISTICS_update (GSF_stats, | ||
1628 | gettext_noop ( | ||
1629 | "# Datastore lookups concluded (load too high)"), | ||
1630 | 1, | ||
1631 | GNUNET_NO); | ||
1632 | call_continuation (pr); | ||
1633 | return; | ||
1634 | } | ||
1635 | start_local_query (pr, uid + 1 /* next_uid */, false /* random */); | ||
1636 | } | ||
1637 | |||
1638 | |||
1639 | /** | ||
1640 | * Is the given target a legitimate peer for forwarding the given request? | ||
1641 | * | ||
1642 | * @param pr request | ||
1643 | * @param target | ||
1644 | * @return #GNUNET_YES if this request could be forwarded to the given peer | ||
1645 | */ | ||
1646 | int | ||
1647 | GSF_pending_request_test_target_ (struct GSF_PendingRequest *pr, | ||
1648 | const struct GNUNET_PeerIdentity *target) | ||
1649 | { | ||
1650 | struct GNUNET_PeerIdentity pi; | ||
1651 | |||
1652 | if (0 == pr->origin_pid) | ||
1653 | return GNUNET_YES; | ||
1654 | GNUNET_PEER_resolve (pr->origin_pid, &pi); | ||
1655 | return (0 == memcmp (&pi, target, sizeof(struct GNUNET_PeerIdentity))) | ||
1656 | ? GNUNET_NO | ||
1657 | : GNUNET_YES; | ||
1658 | } | ||
1659 | |||
1660 | |||
1661 | /** | ||
1662 | * Look up the request in the local datastore. | ||
1663 | * | ||
1664 | * @param pr the pending request to process | ||
1665 | * @param cont function to call at the end | ||
1666 | * @param cont_cls closure for @a cont | ||
1667 | */ | ||
1668 | void | ||
1669 | GSF_local_lookup_ (struct GSF_PendingRequest *pr, | ||
1670 | GSF_LocalLookupContinuation cont, | ||
1671 | void *cont_cls) | ||
1672 | { | ||
1673 | GNUNET_assert (NULL == pr->gh); | ||
1674 | GNUNET_assert (NULL == pr->cadet_request); | ||
1675 | GNUNET_assert (NULL == pr->llc_cont); | ||
1676 | pr->llc_cont = cont; | ||
1677 | pr->llc_cont_cls = cont_cls; | ||
1678 | #if INSANE_STATISTICS | ||
1679 | GNUNET_STATISTICS_update (GSF_stats, | ||
1680 | gettext_noop ("# Datastore lookups initiated"), | ||
1681 | 1, | ||
1682 | GNUNET_NO); | ||
1683 | #endif | ||
1684 | start_local_query (pr, 0 /* next_uid */, true /* random */); | ||
1685 | } | ||
1686 | |||
1687 | |||
1688 | /** | ||
1689 | * Handle P2P "CONTENT" message. Checks that the message is | ||
1690 | * well-formed and then checks if there are any pending requests for | ||
1691 | * this content and possibly passes it on (to local clients or other | ||
1692 | * peers). Does NOT perform migration (content caching at this peer). | ||
1693 | * | ||
1694 | * @param cls the other peer involved | ||
1695 | * @param put the actual message | ||
1696 | */ | ||
1697 | void | ||
1698 | handle_p2p_put (void *cls, | ||
1699 | const struct PutMessage *put) | ||
1700 | { | ||
1701 | struct GSF_ConnectedPeer *cp = cls; | ||
1702 | uint16_t msize; | ||
1703 | size_t dsize; | ||
1704 | enum GNUNET_BLOCK_Type type; | ||
1705 | struct GNUNET_TIME_Absolute expiration; | ||
1706 | struct GNUNET_HashCode query; | ||
1707 | struct ProcessReplyClosure prq; | ||
1708 | struct GNUNET_TIME_Relative block_time; | ||
1709 | double putl; | ||
1710 | struct PutMigrationContext *pmc; | ||
1711 | |||
1712 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1713 | "Received P2P PUT from %s\n", | ||
1714 | GNUNET_i2s (GSF_get_peer_performance_data_ (cp)->peer)); | ||
1715 | GSF_cover_content_count++; | ||
1716 | msize = ntohs (put->header.size); | ||
1717 | dsize = msize - sizeof(struct PutMessage); | ||
1718 | type = ntohl (put->type); | ||
1719 | expiration = GNUNET_TIME_absolute_ntoh (put->expiration); | ||
1720 | /* do not allow migrated content to live longer than 1 year */ | ||
1721 | expiration = GNUNET_TIME_absolute_min (GNUNET_TIME_relative_to_absolute ( | ||
1722 | GNUNET_TIME_UNIT_YEARS), | ||
1723 | expiration); | ||
1724 | if (GNUNET_OK != | ||
1725 | GNUNET_BLOCK_check_block (GSF_block_ctx, | ||
1726 | type, | ||
1727 | &put[1], | ||
1728 | dsize)) | ||
1729 | { | ||
1730 | GNUNET_break_op (0); | ||
1731 | return; | ||
1732 | } | ||
1733 | if (GNUNET_OK != | ||
1734 | GNUNET_BLOCK_get_key (GSF_block_ctx, | ||
1735 | type, | ||
1736 | &put[1], | ||
1737 | dsize, | ||
1738 | &query)) | ||
1739 | { | ||
1740 | GNUNET_break_op (0); | ||
1741 | return; | ||
1742 | } | ||
1743 | GNUNET_STATISTICS_update (GSF_stats, | ||
1744 | gettext_noop ("# GAP PUT messages received"), | ||
1745 | 1, | ||
1746 | GNUNET_NO); | ||
1747 | /* now, lookup 'query' */ | ||
1748 | prq.data = (const void *) &put[1]; | ||
1749 | prq.sender = cp; | ||
1750 | prq.size = dsize; | ||
1751 | prq.type = type; | ||
1752 | prq.expiration = expiration; | ||
1753 | prq.priority = 0; | ||
1754 | prq.anonymity_level = UINT32_MAX; | ||
1755 | prq.request_found = GNUNET_NO; | ||
1756 | GNUNET_CONTAINER_multihashmap_get_multiple (pr_map, | ||
1757 | &query, | ||
1758 | &process_reply, | ||
1759 | &prq); | ||
1760 | if (NULL != cp) | ||
1761 | { | ||
1762 | GSF_connected_peer_change_preference_ (cp, | ||
1763 | CONTENT_BANDWIDTH_VALUE | ||
1764 | + 1000 * prq.priority); | ||
1765 | GSF_get_peer_performance_data_ (cp)->respect += prq.priority; | ||
1766 | } | ||
1767 | if ((GNUNET_YES == active_to_migration) && (NULL != cp) && | ||
1768 | (GNUNET_NO == test_put_load_too_high (prq.priority))) | ||
1769 | { | ||
1770 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1771 | "Replicating result for query `%s' with priority %u\n", | ||
1772 | GNUNET_h2s (&query), | ||
1773 | prq.priority); | ||
1774 | pmc = GNUNET_new (struct PutMigrationContext); | ||
1775 | pmc->start = GNUNET_TIME_absolute_get (); | ||
1776 | pmc->requested = prq.request_found; | ||
1777 | GNUNET_assert (0 != GSF_get_peer_performance_data_ (cp)->pid); | ||
1778 | GNUNET_PEER_resolve (GSF_get_peer_performance_data_ (cp)->pid, | ||
1779 | &pmc->origin); | ||
1780 | if (NULL == GNUNET_DATASTORE_put (GSF_dsh, | ||
1781 | 0, | ||
1782 | &query, | ||
1783 | dsize, | ||
1784 | &put[1], | ||
1785 | type, | ||
1786 | prq.priority, | ||
1787 | 1 /* anonymity */, | ||
1788 | 0 /* replication */, | ||
1789 | expiration, | ||
1790 | 1 + prq.priority, | ||
1791 | MAX_DATASTORE_QUEUE, | ||
1792 | &put_migration_continuation, | ||
1793 | pmc)) | ||
1794 | { | ||
1795 | put_migration_continuation (pmc, | ||
1796 | GNUNET_SYSERR, | ||
1797 | GNUNET_TIME_UNIT_ZERO_ABS, | ||
1798 | NULL); | ||
1799 | } | ||
1800 | } | ||
1801 | else if (NULL != cp) | ||
1802 | { | ||
1803 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1804 | "Choosing not to keep content `%s' (%d/%d)\n", | ||
1805 | GNUNET_h2s (&query), | ||
1806 | active_to_migration, | ||
1807 | test_put_load_too_high (prq.priority)); | ||
1808 | } | ||
1809 | putl = GNUNET_LOAD_get_load (datastore_put_load); | ||
1810 | if ((NULL != cp) && (GNUNET_NO == prq.request_found) && | ||
1811 | ((GNUNET_YES != active_to_migration) || | ||
1812 | (putl > 2.5 * (1 + prq.priority)))) | ||
1813 | { | ||
1814 | if (GNUNET_YES != active_to_migration) | ||
1815 | putl = 1.0 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 5); | ||
1816 | block_time = GNUNET_TIME_relative_multiply ( | ||
1817 | GNUNET_TIME_UNIT_MILLISECONDS, | ||
1818 | 5000 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1819 | (unsigned int) (60000 * putl * putl))); | ||
1820 | GNUNET_log ( | ||
1821 | GNUNET_ERROR_TYPE_DEBUG, | ||
1822 | "Asking to stop migration for %s because of load %f and events %d/%d\n", | ||
1823 | GNUNET_STRINGS_relative_time_to_string (block_time, GNUNET_YES), | ||
1824 | putl, | ||
1825 | active_to_migration, | ||
1826 | (GNUNET_NO == prq.request_found)); | ||
1827 | GSF_block_peer_migration_ (cp, | ||
1828 | GNUNET_TIME_relative_to_absolute (block_time)); | ||
1829 | } | ||
1830 | } | ||
1831 | |||
1832 | |||
1833 | /** | ||
1834 | * Check if the given request is still active. | ||
1835 | * | ||
1836 | * @param pr pending request | ||
1837 | * @return #GNUNET_YES if the request is still active | ||
1838 | */ | ||
1839 | int | ||
1840 | GSF_pending_request_test_active_ (struct GSF_PendingRequest *pr) | ||
1841 | { | ||
1842 | return (NULL != pr->rh) ? GNUNET_YES : GNUNET_NO; | ||
1843 | } | ||
1844 | |||
1845 | |||
1846 | /** | ||
1847 | * Setup the subsystem. | ||
1848 | */ | ||
1849 | void | ||
1850 | GSF_pending_request_init_ () | ||
1851 | { | ||
1852 | if (GNUNET_OK != | ||
1853 | GNUNET_CONFIGURATION_get_value_number (GSF_cfg, | ||
1854 | "fs", | ||
1855 | "MAX_PENDING_REQUESTS", | ||
1856 | &max_pending_requests)) | ||
1857 | { | ||
1858 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, | ||
1859 | "fs", | ||
1860 | "MAX_PENDING_REQUESTS"); | ||
1861 | } | ||
1862 | active_to_migration = | ||
1863 | GNUNET_CONFIGURATION_get_value_yesno (GSF_cfg, "FS", "CONTENT_CACHING"); | ||
1864 | datastore_put_load = GNUNET_LOAD_value_init (DATASTORE_LOAD_AUTODECLINE); | ||
1865 | pr_map = GNUNET_CONTAINER_multihashmap_create (32 * 1024, GNUNET_YES); | ||
1866 | requests_by_expiration_heap = | ||
1867 | GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); | ||
1868 | } | ||
1869 | |||
1870 | |||
1871 | /** | ||
1872 | * Shutdown the subsystem. | ||
1873 | */ | ||
1874 | void | ||
1875 | GSF_pending_request_done_ () | ||
1876 | { | ||
1877 | GNUNET_CONTAINER_multihashmap_iterate (pr_map, &clean_request, NULL); | ||
1878 | GNUNET_CONTAINER_multihashmap_destroy (pr_map); | ||
1879 | pr_map = NULL; | ||
1880 | GNUNET_CONTAINER_heap_destroy (requests_by_expiration_heap); | ||
1881 | requests_by_expiration_heap = NULL; | ||
1882 | GNUNET_LOAD_value_free (datastore_put_load); | ||
1883 | datastore_put_load = NULL; | ||
1884 | } | ||
1885 | |||
1886 | |||
1887 | /* 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 | */ | ||
35 | enum 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 | */ | ||
81 | struct 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 | */ | ||
186 | typedef 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 | */ | ||
221 | struct GSF_PendingRequest * | ||
222 | GSF_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 | */ | ||
247 | void | ||
248 | GSF_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 | */ | ||
259 | struct GSF_PendingRequestData * | ||
260 | GSF_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 | */ | ||
269 | int | ||
270 | GSF_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 | */ | ||
282 | int | ||
283 | GSF_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 | */ | ||
294 | struct GNUNET_MQ_Envelope * | ||
295 | GSF_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 | */ | ||
304 | void | ||
305 | GSF_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 | */ | ||
318 | typedef 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 | */ | ||
330 | void | ||
331 | GSF_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 | */ | ||
344 | void | ||
345 | handle_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 | */ | ||
354 | void | ||
355 | GSF_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 | */ | ||
363 | void | ||
364 | GSF_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 | */ | ||
375 | typedef 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 | */ | ||
389 | void | ||
390 | GSF_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 | */ | ||
402 | int | ||
403 | GSF_pending_request_test_target_ (struct GSF_PendingRequest *pr, | ||
404 | const struct GNUNET_PeerIdentity *target); | ||
405 | |||
406 | |||
407 | /** | ||
408 | * Setup the subsystem. | ||
409 | */ | ||
410 | void | ||
411 | GSF_pending_request_init_ (void); | ||
412 | |||
413 | |||
414 | /** | ||
415 | * Shutdown the subsystem. | ||
416 | */ | ||
417 | void | ||
418 | GSF_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 92dbba8e6..000000000 --- a/src/fs/gnunet-service-fs_push.c +++ /dev/null | |||
@@ -1,672 +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 | */ | ||
59 | struct 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 | */ | ||
107 | struct 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 | */ | ||
134 | static struct MigrationReadyBlock *mig_head; | ||
135 | |||
136 | /** | ||
137 | * Tail of linked list of blocks that can be migrated. | ||
138 | */ | ||
139 | static struct MigrationReadyBlock *mig_tail; | ||
140 | |||
141 | /** | ||
142 | * Head of linked list of peers. | ||
143 | */ | ||
144 | static struct MigrationReadyPeer *peer_head; | ||
145 | |||
146 | /** | ||
147 | * Tail of linked list of peers. | ||
148 | */ | ||
149 | static struct MigrationReadyPeer *peer_tail; | ||
150 | |||
151 | /** | ||
152 | * Request to datastore for migration (or NULL). | ||
153 | */ | ||
154 | static struct GNUNET_DATASTORE_QueueEntry *mig_qe; | ||
155 | |||
156 | /** | ||
157 | * ID of task that collects blocks for migration. | ||
158 | */ | ||
159 | static 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 | */ | ||
165 | static struct GNUNET_TIME_Relative min_migration_delay; | ||
166 | |||
167 | /** | ||
168 | * Size of the doubly-linked list of migration blocks. | ||
169 | */ | ||
170 | static unsigned int mig_size; | ||
171 | |||
172 | /** | ||
173 | * Is this module enabled? | ||
174 | */ | ||
175 | static int enabled; | ||
176 | |||
177 | /** | ||
178 | * Did we find anything in the datastore? | ||
179 | */ | ||
180 | static int value_found; | ||
181 | |||
182 | |||
183 | /** | ||
184 | * Delete the given migration block. | ||
185 | * | ||
186 | * @param mb block to delete | ||
187 | */ | ||
188 | static void | ||
189 | delete_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 *` to find content for | ||
205 | */ | ||
206 | static void | ||
207 | find_content (void *cls); | ||
208 | |||
209 | |||
210 | /** | ||
211 | * Send the given block to the given peer. | ||
212 | * | ||
213 | * @param mrp target peer | ||
214 | * @param block the block | ||
215 | * @return #GNUNET_YES if the block was deleted (!) | ||
216 | */ | ||
217 | static int | ||
218 | transmit_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 | */ | ||
273 | static unsigned int | ||
274 | count_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 | */ | ||
293 | static long | ||
294 | score_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 | */ | ||
324 | static void | ||
325 | consider_gathering (void); | ||
326 | |||
327 | |||
328 | static void | ||
329 | find_content (void *cls) | ||
330 | { | ||
331 | struct MigrationReadyPeer *mrp = cls; | ||
332 | struct MigrationReadyBlock *pos; | ||
333 | long score; | ||
334 | long best_score; | ||
335 | struct MigrationReadyBlock *best; | ||
336 | |||
337 | mrp->env = NULL; | ||
338 | best = NULL; | ||
339 | best_score = -1; | ||
340 | pos = mig_head; | ||
341 | while (NULL != pos) | ||
342 | { | ||
343 | score = score_content (mrp, pos); | ||
344 | if (score > best_score) | ||
345 | { | ||
346 | best_score = score; | ||
347 | best = pos; | ||
348 | } | ||
349 | pos = pos->next; | ||
350 | } | ||
351 | if (NULL == best) | ||
352 | { | ||
353 | if (mig_size < MAX_MIGRATION_QUEUE) | ||
354 | { | ||
355 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
356 | "No content found for pushing, waiting for queue to fill\n"); | ||
357 | return; /* will fill up eventually... */ | ||
358 | } | ||
359 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
360 | "No suitable content found, purging content from full queue\n"); | ||
361 | /* failed to find migration target AND | ||
362 | * queue is full, purge most-forwarded | ||
363 | * block from queue to make room for more */ | ||
364 | pos = mig_head; | ||
365 | while (NULL != pos) | ||
366 | { | ||
367 | score = count_targets (pos); | ||
368 | if (score >= best_score) | ||
369 | { | ||
370 | best_score = score; | ||
371 | best = pos; | ||
372 | } | ||
373 | pos = pos->next; | ||
374 | } | ||
375 | GNUNET_assert (NULL != best); | ||
376 | delete_migration_block (best); | ||
377 | consider_gathering (); | ||
378 | return; | ||
379 | } | ||
380 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
381 | "Preparing to push best content to peer\n"); | ||
382 | transmit_content (mrp, | ||
383 | best); | ||
384 | } | ||
385 | |||
386 | |||
387 | /** | ||
388 | * Task that is run periodically to obtain blocks for content | ||
389 | * migration | ||
390 | * | ||
391 | * @param cls unused | ||
392 | */ | ||
393 | static void | ||
394 | gather_migration_blocks (void *cls); | ||
395 | |||
396 | |||
397 | /** | ||
398 | * If the migration task is not currently running, consider | ||
399 | * (re)scheduling it with the appropriate delay. | ||
400 | */ | ||
401 | static void | ||
402 | consider_gathering () | ||
403 | { | ||
404 | struct GNUNET_TIME_Relative delay; | ||
405 | |||
406 | if (NULL == GSF_dsh) | ||
407 | return; | ||
408 | if (NULL != mig_qe) | ||
409 | return; | ||
410 | if (NULL != mig_task) | ||
411 | return; | ||
412 | if (mig_size >= MAX_MIGRATION_QUEUE) | ||
413 | return; | ||
414 | delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, | ||
415 | mig_size); | ||
416 | delay = GNUNET_TIME_relative_divide (delay, | ||
417 | MAX_MIGRATION_QUEUE); | ||
418 | delay = GNUNET_TIME_relative_max (delay, | ||
419 | min_migration_delay); | ||
420 | if (GNUNET_NO == value_found) | ||
421 | { | ||
422 | /* wait at least 5s if the datastore is empty */ | ||
423 | delay = GNUNET_TIME_relative_max (delay, | ||
424 | GNUNET_TIME_relative_multiply ( | ||
425 | GNUNET_TIME_UNIT_SECONDS, | ||
426 | 5)); | ||
427 | } | ||
428 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
429 | "Scheduling gathering task (queue size: %u)\n", | ||
430 | mig_size); | ||
431 | mig_task = GNUNET_SCHEDULER_add_delayed (delay, | ||
432 | &gather_migration_blocks, | ||
433 | NULL); | ||
434 | } | ||
435 | |||
436 | |||
437 | /** | ||
438 | * Process content offered for migration. | ||
439 | * | ||
440 | * @param cls closure | ||
441 | * @param key key for the content | ||
442 | * @param size number of bytes in data | ||
443 | * @param data content stored | ||
444 | * @param type type of the content | ||
445 | * @param priority priority of the content | ||
446 | * @param anonymity anonymity-level for the content | ||
447 | * @param replication replication-level for the content | ||
448 | * @param expiration expiration time for the content | ||
449 | * @param uid unique identifier for the datum; | ||
450 | * maybe 0 if no unique identifier is available | ||
451 | */ | ||
452 | static void | ||
453 | process_migration_content (void *cls, | ||
454 | const struct GNUNET_HashCode *key, | ||
455 | size_t size, | ||
456 | const void *data, | ||
457 | enum GNUNET_BLOCK_Type type, | ||
458 | uint32_t priority, | ||
459 | uint32_t anonymity, | ||
460 | uint32_t replication, | ||
461 | struct GNUNET_TIME_Absolute expiration, | ||
462 | uint64_t uid) | ||
463 | { | ||
464 | struct MigrationReadyBlock *mb; | ||
465 | struct MigrationReadyPeer *pos; | ||
466 | |||
467 | mig_qe = NULL; | ||
468 | if (NULL == key) | ||
469 | { | ||
470 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
471 | "No content found for migration...\n"); | ||
472 | consider_gathering (); | ||
473 | return; | ||
474 | } | ||
475 | value_found = GNUNET_YES; | ||
476 | if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us < | ||
477 | MIN_MIGRATION_CONTENT_LIFETIME.rel_value_us) | ||
478 | { | ||
479 | /* content will expire soon, don't bother */ | ||
480 | consider_gathering (); | ||
481 | return; | ||
482 | } | ||
483 | if (type == GNUNET_BLOCK_TYPE_FS_ONDEMAND) | ||
484 | { | ||
485 | if (GNUNET_OK != | ||
486 | GNUNET_FS_handle_on_demand_block (key, | ||
487 | size, | ||
488 | data, | ||
489 | type, | ||
490 | priority, | ||
491 | anonymity, | ||
492 | replication, | ||
493 | expiration, | ||
494 | uid, | ||
495 | &process_migration_content, | ||
496 | NULL)) | ||
497 | consider_gathering (); | ||
498 | return; | ||
499 | } | ||
500 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
501 | "Retrieved block `%s' of type %u for migration (queue size: %u/%u)\n", | ||
502 | GNUNET_h2s (key), | ||
503 | type, mig_size + 1, | ||
504 | MAX_MIGRATION_QUEUE); | ||
505 | mb = GNUNET_malloc (sizeof(struct MigrationReadyBlock) + size); | ||
506 | mb->query = *key; | ||
507 | mb->expiration = expiration; | ||
508 | mb->size = size; | ||
509 | mb->type = type; | ||
510 | GNUNET_memcpy (&mb[1], data, size); | ||
511 | GNUNET_CONTAINER_DLL_insert_after (mig_head, | ||
512 | mig_tail, | ||
513 | mig_tail, | ||
514 | mb); | ||
515 | mig_size++; | ||
516 | for (pos = peer_head; NULL != pos; pos = pos->next) | ||
517 | { | ||
518 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
519 | "Preparing to push best content to peer %s\n", | ||
520 | GNUNET_i2s (GSF_connected_peer_get_identity2_ (pos->peer))); | ||
521 | if ((NULL == pos->env) && | ||
522 | (GNUNET_YES == transmit_content (pos, | ||
523 | mb))) | ||
524 | { | ||
525 | break; /* 'mb' was freed! */ | ||
526 | } | ||
527 | } | ||
528 | consider_gathering (); | ||
529 | } | ||
530 | |||
531 | |||
532 | /** | ||
533 | * Task that is run periodically to obtain blocks for content | ||
534 | * migration | ||
535 | * | ||
536 | * @param cls unused | ||
537 | */ | ||
538 | static void | ||
539 | gather_migration_blocks (void *cls) | ||
540 | { | ||
541 | mig_task = NULL; | ||
542 | if (mig_size >= MAX_MIGRATION_QUEUE) | ||
543 | return; | ||
544 | if (NULL == GSF_dsh) | ||
545 | return; | ||
546 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
547 | "Asking datastore for content for replication (queue size: %u)\n", | ||
548 | mig_size); | ||
549 | value_found = GNUNET_NO; | ||
550 | mig_qe = GNUNET_DATASTORE_get_for_replication (GSF_dsh, | ||
551 | 0, | ||
552 | UINT_MAX, | ||
553 | &process_migration_content, | ||
554 | NULL); | ||
555 | if (NULL == mig_qe) | ||
556 | consider_gathering (); | ||
557 | } | ||
558 | |||
559 | |||
560 | /** | ||
561 | * A peer connected to us. Start pushing content | ||
562 | * to this peer. | ||
563 | * | ||
564 | * @param peer handle for the peer that connected | ||
565 | */ | ||
566 | void | ||
567 | GSF_push_start_ (struct GSF_ConnectedPeer *peer) | ||
568 | { | ||
569 | struct MigrationReadyPeer *mrp; | ||
570 | |||
571 | if (GNUNET_YES != enabled) | ||
572 | return; | ||
573 | for (mrp = peer_head; NULL != mrp; mrp = mrp->next) | ||
574 | if (mrp->peer == peer) | ||
575 | break; | ||
576 | if (NULL != mrp) | ||
577 | { | ||
578 | /* same peer added twice, must not happen */ | ||
579 | GNUNET_break (0); | ||
580 | return; | ||
581 | } | ||
582 | |||
583 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
584 | "Adding peer %s to list for pushing\n", | ||
585 | GNUNET_i2s (GSF_connected_peer_get_identity2_ (peer))); | ||
586 | |||
587 | mrp = GNUNET_new (struct MigrationReadyPeer); | ||
588 | mrp->peer = peer; | ||
589 | find_content (mrp); | ||
590 | GNUNET_CONTAINER_DLL_insert (peer_head, | ||
591 | peer_tail, | ||
592 | mrp); | ||
593 | } | ||
594 | |||
595 | |||
596 | /** | ||
597 | * A peer disconnected from us. Stop pushing content | ||
598 | * to this peer. | ||
599 | * | ||
600 | * @param peer handle for the peer that disconnected | ||
601 | */ | ||
602 | void | ||
603 | GSF_push_stop_ (struct GSF_ConnectedPeer *peer) | ||
604 | { | ||
605 | struct MigrationReadyPeer *pos; | ||
606 | |||
607 | for (pos = peer_head; NULL != pos; pos = pos->next) | ||
608 | if (pos->peer == peer) | ||
609 | break; | ||
610 | if (NULL == pos) | ||
611 | return; | ||
612 | if (NULL != pos->env) | ||
613 | GNUNET_MQ_send_cancel (pos->env); | ||
614 | GNUNET_CONTAINER_DLL_remove (peer_head, | ||
615 | peer_tail, | ||
616 | pos); | ||
617 | GNUNET_free (pos); | ||
618 | } | ||
619 | |||
620 | |||
621 | /** | ||
622 | * Setup the module. | ||
623 | */ | ||
624 | void | ||
625 | GSF_push_init_ () | ||
626 | { | ||
627 | enabled = | ||
628 | GNUNET_CONFIGURATION_get_value_yesno (GSF_cfg, | ||
629 | "FS", | ||
630 | "CONTENT_PUSHING"); | ||
631 | if (GNUNET_YES != enabled) | ||
632 | return; | ||
633 | |||
634 | if (GNUNET_OK != | ||
635 | GNUNET_CONFIGURATION_get_value_time (GSF_cfg, | ||
636 | "fs", | ||
637 | "MIN_MIGRATION_DELAY", | ||
638 | &min_migration_delay)) | ||
639 | { | ||
640 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, | ||
641 | "fs", | ||
642 | "MIN_MIGRATION_DELAY", | ||
643 | _ ("time required, content pushing disabled")); | ||
644 | return; | ||
645 | } | ||
646 | consider_gathering (); | ||
647 | } | ||
648 | |||
649 | |||
650 | /** | ||
651 | * Shutdown the module. | ||
652 | */ | ||
653 | void | ||
654 | GSF_push_done_ () | ||
655 | { | ||
656 | if (NULL != mig_task) | ||
657 | { | ||
658 | GNUNET_SCHEDULER_cancel (mig_task); | ||
659 | mig_task = NULL; | ||
660 | } | ||
661 | if (NULL != mig_qe) | ||
662 | { | ||
663 | GNUNET_DATASTORE_cancel (mig_qe); | ||
664 | mig_qe = NULL; | ||
665 | } | ||
666 | while (NULL != mig_head) | ||
667 | delete_migration_block (mig_head); | ||
668 | GNUNET_assert (0 == mig_size); | ||
669 | } | ||
670 | |||
671 | |||
672 | /* 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 | */ | ||
35 | void | ||
36 | GSF_push_init_ (void); | ||
37 | |||
38 | |||
39 | /** | ||
40 | * Shutdown the module. | ||
41 | */ | ||
42 | void | ||
43 | GSF_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 | */ | ||
52 | void | ||
53 | GSF_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 | */ | ||
62 | void | ||
63 | GSF_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 ca2c85724..000000000 --- a/src/fs/gnunet-service-fs_put.c +++ /dev/null | |||
@@ -1,290 +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 | */ | ||
46 | struct 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 | */ | ||
90 | static 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 | */ | ||
101 | static void | ||
102 | gather_dht_put_blocks (void *cls); | ||
103 | |||
104 | |||
105 | /** | ||
106 | * Calculate when to run the next PUT operation and schedule it. | ||
107 | * | ||
108 | * @param po put operator to schedule | ||
109 | */ | ||
110 | static void | ||
111 | schedule_next_put (struct PutOperator *po) | ||
112 | { | ||
113 | struct GNUNET_TIME_Relative delay; | ||
114 | |||
115 | if (po->zero_anonymity_count_estimate > 0) | ||
116 | { | ||
117 | delay = | ||
118 | GNUNET_TIME_relative_divide (GNUNET_DHT_DEFAULT_REPUBLISH_FREQUENCY, | ||
119 | po->zero_anonymity_count_estimate); | ||
120 | delay = GNUNET_TIME_relative_min (delay, MAX_DHT_PUT_FREQ); | ||
121 | } | ||
122 | else | ||
123 | { | ||
124 | /* if we have NO zero-anonymity content yet, wait 5 minutes for some to | ||
125 | * (hopefully) appear */ | ||
126 | delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5); | ||
127 | } | ||
128 | po->dht_task = | ||
129 | GNUNET_SCHEDULER_add_delayed (delay, &gather_dht_put_blocks, po); | ||
130 | } | ||
131 | |||
132 | |||
133 | /** | ||
134 | * Continuation called after DHT PUT operation has finished. | ||
135 | * | ||
136 | * @param cls type of blocks to gather | ||
137 | */ | ||
138 | static void | ||
139 | delay_dht_put_blocks (void *cls) | ||
140 | { | ||
141 | struct PutOperator *po = cls; | ||
142 | |||
143 | po->dht_put = NULL; | ||
144 | schedule_next_put (po); | ||
145 | } | ||
146 | |||
147 | |||
148 | /** | ||
149 | * Task that is run periodically to obtain blocks for DHT PUTs. | ||
150 | * | ||
151 | * @param cls type of blocks to gather | ||
152 | */ | ||
153 | static void | ||
154 | delay_dht_put_task (void *cls) | ||
155 | { | ||
156 | struct PutOperator *po = cls; | ||
157 | |||
158 | po->dht_task = NULL; | ||
159 | schedule_next_put (po); | ||
160 | } | ||
161 | |||
162 | |||
163 | /** | ||
164 | * Store content in DHT. | ||
165 | * | ||
166 | * @param cls closure | ||
167 | * @param key key for the content | ||
168 | * @param size number of bytes in data | ||
169 | * @param data content stored | ||
170 | * @param type type of the content | ||
171 | * @param priority priority of the content | ||
172 | * @param anonymity anonymity-level for the content | ||
173 | * @param replication replication-level for the content | ||
174 | * @param expiration expiration time for the content | ||
175 | * @param uid unique identifier for the datum; | ||
176 | * maybe 0 if no unique identifier is available | ||
177 | */ | ||
178 | static void | ||
179 | process_dht_put_content (void *cls, | ||
180 | const struct GNUNET_HashCode *key, | ||
181 | size_t size, | ||
182 | const void *data, | ||
183 | enum GNUNET_BLOCK_Type type, | ||
184 | uint32_t priority, | ||
185 | uint32_t anonymity, | ||
186 | uint32_t replication, | ||
187 | struct GNUNET_TIME_Absolute expiration, | ||
188 | uint64_t uid) | ||
189 | { | ||
190 | struct PutOperator *po = cls; | ||
191 | |||
192 | po->dht_qe = NULL; | ||
193 | if (key == NULL) | ||
194 | { | ||
195 | po->zero_anonymity_count_estimate = po->result_count; | ||
196 | po->result_count = 0; | ||
197 | po->next_uid = 0; | ||
198 | po->dht_task = GNUNET_SCHEDULER_add_now (&delay_dht_put_task, po); | ||
199 | return; | ||
200 | } | ||
201 | po->result_count++; | ||
202 | po->next_uid = uid + 1; | ||
203 | po->zero_anonymity_count_estimate = | ||
204 | GNUNET_MAX (po->result_count, po->zero_anonymity_count_estimate); | ||
205 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
206 | "Retrieved block `%s' of type %u for DHT PUT\n", GNUNET_h2s (key), | ||
207 | type); | ||
208 | po->dht_put = GNUNET_DHT_put (GSF_dht, | ||
209 | key, | ||
210 | DEFAULT_PUT_REPLICATION, | ||
211 | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, | ||
212 | type, | ||
213 | size, | ||
214 | data, | ||
215 | expiration, | ||
216 | &delay_dht_put_blocks, | ||
217 | po); | ||
218 | } | ||
219 | |||
220 | |||
221 | static void | ||
222 | gather_dht_put_blocks (void *cls) | ||
223 | { | ||
224 | struct PutOperator *po = cls; | ||
225 | |||
226 | po->dht_task = NULL; | ||
227 | po->dht_qe = | ||
228 | GNUNET_DATASTORE_get_zero_anonymity (GSF_dsh, | ||
229 | po->next_uid, | ||
230 | 0, | ||
231 | UINT_MAX, | ||
232 | po->dht_put_type, | ||
233 | &process_dht_put_content, | ||
234 | po); | ||
235 | if (NULL == po->dht_qe) | ||
236 | po->dht_task = GNUNET_SCHEDULER_add_now (&delay_dht_put_task, po); | ||
237 | } | ||
238 | |||
239 | |||
240 | /** | ||
241 | * Setup the module. | ||
242 | */ | ||
243 | void | ||
244 | GSF_put_init_ () | ||
245 | { | ||
246 | unsigned int i; | ||
247 | |||
248 | i = 0; | ||
249 | while (operators[i].dht_put_type != GNUNET_BLOCK_TYPE_ANY) | ||
250 | { | ||
251 | operators[i].dht_task = | ||
252 | GNUNET_SCHEDULER_add_now (&gather_dht_put_blocks, &operators[i]); | ||
253 | i++; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | |||
258 | /** | ||
259 | * Shutdown the module. | ||
260 | */ | ||
261 | void | ||
262 | GSF_put_done_ () | ||
263 | { | ||
264 | struct PutOperator *po; | ||
265 | unsigned int i; | ||
266 | |||
267 | i = 0; | ||
268 | while ((po = &operators[i])->dht_put_type != GNUNET_BLOCK_TYPE_ANY) | ||
269 | { | ||
270 | if (NULL != po->dht_task) | ||
271 | { | ||
272 | GNUNET_SCHEDULER_cancel (po->dht_task); | ||
273 | po->dht_task = NULL; | ||
274 | } | ||
275 | if (NULL != po->dht_put) | ||
276 | { | ||
277 | GNUNET_DHT_put_cancel (po->dht_put); | ||
278 | po->dht_put = NULL; | ||
279 | } | ||
280 | if (NULL != po->dht_qe) | ||
281 | { | ||
282 | GNUNET_DATASTORE_cancel (po->dht_qe); | ||
283 | po->dht_qe = NULL; | ||
284 | } | ||
285 | i++; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | |||
290 | /* 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 | */ | ||
35 | void | ||
36 | GSF_put_init_ (void); | ||
37 | |||
38 | |||
39 | /** | ||
40 | * Shutdown the module. | ||
41 | */ | ||
42 | void | ||
43 | GSF_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 326f75a63..000000000 --- a/src/fs/gnunet-unindex.c +++ /dev/null | |||
@@ -1,206 +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 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | |||
32 | static int ret; | ||
33 | |||
34 | static unsigned int verbose; | ||
35 | |||
36 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
37 | |||
38 | static struct GNUNET_FS_Handle *ctx; | ||
39 | |||
40 | static struct GNUNET_FS_UnindexContext *uc; | ||
41 | |||
42 | |||
43 | static void | ||
44 | cleanup_task (void *cls) | ||
45 | { | ||
46 | GNUNET_FS_stop (ctx); | ||
47 | ctx = NULL; | ||
48 | } | ||
49 | |||
50 | |||
51 | static void | ||
52 | shutdown_task (void *cls) | ||
53 | { | ||
54 | struct GNUNET_FS_UnindexContext *u; | ||
55 | |||
56 | if (uc != NULL) | ||
57 | { | ||
58 | u = uc; | ||
59 | uc = NULL; | ||
60 | GNUNET_FS_unindex_stop (u); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Called by FS client to give information about the progress of an | ||
67 | * operation. | ||
68 | * | ||
69 | * @param cls closure | ||
70 | * @param info details about the event, specifying the event type | ||
71 | * and various bits about the event | ||
72 | * @return client-context (for the next progress call | ||
73 | * for this operation; should be set to NULL for | ||
74 | * SUSPEND and STOPPED events). The value returned | ||
75 | * will be passed to future callbacks in the respective | ||
76 | * field in the GNUNET_FS_ProgressInfo struct. | ||
77 | */ | ||
78 | static void * | ||
79 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) | ||
80 | { | ||
81 | const char *s; | ||
82 | |||
83 | switch (info->status) | ||
84 | { | ||
85 | case GNUNET_FS_STATUS_UNINDEX_START: | ||
86 | break; | ||
87 | |||
88 | case GNUNET_FS_STATUS_UNINDEX_PROGRESS: | ||
89 | if (verbose) | ||
90 | { | ||
91 | s = GNUNET_STRINGS_relative_time_to_string (info->value.unindex.eta, | ||
92 | GNUNET_YES); | ||
93 | fprintf (stdout, | ||
94 | _ ("Unindexing at %llu/%llu (%s remaining)\n"), | ||
95 | (unsigned long long) info->value.unindex.completed, | ||
96 | (unsigned long long) info->value.unindex.size, | ||
97 | s); | ||
98 | } | ||
99 | break; | ||
100 | |||
101 | case GNUNET_FS_STATUS_UNINDEX_ERROR: | ||
102 | fprintf (stderr, | ||
103 | _ ("Error unindexing: %s.\n"), | ||
104 | info->value.unindex.specifics.error.message); | ||
105 | GNUNET_SCHEDULER_shutdown (); | ||
106 | break; | ||
107 | |||
108 | case GNUNET_FS_STATUS_UNINDEX_COMPLETED: | ||
109 | fprintf (stdout, "%s", _ ("Unindexing done.\n")); | ||
110 | GNUNET_SCHEDULER_shutdown (); | ||
111 | break; | ||
112 | |||
113 | case GNUNET_FS_STATUS_UNINDEX_STOPPED: | ||
114 | GNUNET_SCHEDULER_add_now (&cleanup_task, NULL); | ||
115 | break; | ||
116 | |||
117 | default: | ||
118 | fprintf (stderr, _ ("Unexpected status: %d\n"), info->status); | ||
119 | break; | ||
120 | } | ||
121 | return NULL; | ||
122 | } | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Main function that will be run by the scheduler. | ||
127 | * | ||
128 | * @param cls closure | ||
129 | * @param args remaining command-line arguments | ||
130 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
131 | * @param c configuration | ||
132 | */ | ||
133 | static void | ||
134 | run (void *cls, | ||
135 | char *const *args, | ||
136 | const char *cfgfile, | ||
137 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
138 | { | ||
139 | /* check arguments */ | ||
140 | if ((args[0] == NULL) || (args[1] != NULL)) | ||
141 | { | ||
142 | printf (_ ("You must specify one and only one filename for unindexing.\n")); | ||
143 | ret = -1; | ||
144 | return; | ||
145 | } | ||
146 | cfg = c; | ||
147 | ctx = GNUNET_FS_start (cfg, | ||
148 | "gnunet-unindex", | ||
149 | &progress_cb, | ||
150 | NULL, | ||
151 | GNUNET_FS_FLAGS_NONE, | ||
152 | GNUNET_FS_OPTIONS_END); | ||
153 | if (NULL == ctx) | ||
154 | { | ||
155 | fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS"); | ||
156 | ret = 1; | ||
157 | return; | ||
158 | } | ||
159 | uc = GNUNET_FS_unindex_start (ctx, args[0], NULL); | ||
160 | if (NULL == uc) | ||
161 | { | ||
162 | fprintf (stderr, "%s", _ ("Could not start unindex operation.\n")); | ||
163 | GNUNET_FS_stop (ctx); | ||
164 | return; | ||
165 | } | ||
166 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
167 | } | ||
168 | |||
169 | |||
170 | /** | ||
171 | * The main function to unindex content. | ||
172 | * | ||
173 | * @param argc number of arguments from the command line | ||
174 | * @param argv command line arguments | ||
175 | * @return 0 ok, 1 on error | ||
176 | */ | ||
177 | int | ||
178 | main (int argc, char *const *argv) | ||
179 | { | ||
180 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
181 | GNUNET_GETOPT_option_verbose (&verbose), | ||
182 | |||
183 | GNUNET_GETOPT_OPTION_END | ||
184 | }; | ||
185 | |||
186 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
187 | return 2; | ||
188 | |||
189 | ret = (GNUNET_OK == | ||
190 | GNUNET_PROGRAM_run ( | ||
191 | argc, | ||
192 | argv, | ||
193 | "gnunet-unindex [OPTIONS] FILENAME", | ||
194 | gettext_noop ( | ||
195 | "Unindex a file that was previously indexed with gnunet-publish."), | ||
196 | options, | ||
197 | &run, | ||
198 | NULL)) | ||
199 | ? ret | ||
200 | : 1; | ||
201 | GNUNET_free_nz ((void *) argv); | ||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | |||
206 | /* end of gnunet-unindex.c */ | ||
diff --git a/src/fs/meson.build b/src/fs/meson.build deleted file mode 100644 index 4246e4fcf..000000000 --- a/src/fs/meson.build +++ /dev/null | |||
@@ -1,141 +0,0 @@ | |||
1 | libgnunetfs_src = ['fs_api.c', | ||
2 | 'fs_directory.c', | ||
3 | 'fs_dirmetascan.c', | ||
4 | 'fs_download.c', | ||
5 | 'fs_file_information.c', | ||
6 | 'fs_getopt.c', | ||
7 | 'fs_list_indexed.c', | ||
8 | 'fs_publish.c', | ||
9 | 'fs_publish_ksk.c', | ||
10 | 'fs_publish_ublock.c', | ||
11 | 'fs_misc.c', | ||
12 | 'fs_namespace.c', | ||
13 | 'fs_search.c', | ||
14 | 'fs_sharetree.c', | ||
15 | 'fs_tree.c', | ||
16 | 'fs_unindex.c', | ||
17 | 'fs_uri.c', | ||
18 | 'meta_data.c'] | ||
19 | |||
20 | gnunetservicefs_src = ['gnunet-service-fs.c', | ||
21 | 'gnunet-service-fs_cp.c', | ||
22 | 'gnunet-service-fs_indexing.c', | ||
23 | 'gnunet-service-fs_pe.c', | ||
24 | 'gnunet-service-fs_pr.c', | ||
25 | 'gnunet-service-fs_push.c', | ||
26 | 'gnunet-service-fs_put.c', | ||
27 | 'gnunet-service-fs_cadet_client.c', | ||
28 | 'gnunet-service-fs_cadet_server.c'] | ||
29 | |||
30 | configure_file(input : 'fs.conf.in', | ||
31 | output : 'fs.conf', | ||
32 | configuration : cdata, | ||
33 | install: true, | ||
34 | install_dir: pkgcfgdir) | ||
35 | |||
36 | |||
37 | if get_option('monolith') | ||
38 | foreach p : libgnunetfs_src + gnunetservicefs_src | ||
39 | gnunet_src += 'fs/' + p | ||
40 | endforeach | ||
41 | subdir_done() | ||
42 | endif | ||
43 | |||
44 | libgnunetfs = library('gnunetfs', | ||
45 | libgnunetfs_src, | ||
46 | soversion: '2', | ||
47 | version: '2.1.1', | ||
48 | dependencies: [libgnunetutil_dep, | ||
49 | libgnunetdatastore_dep, | ||
50 | libgnunetstatistics_dep, | ||
51 | unistr_dep], | ||
52 | include_directories: [incdir, configuration_inc], | ||
53 | install: true, | ||
54 | install_dir: get_option('libdir')) | ||
55 | libgnunetfs_dep = declare_dependency(link_with : libgnunetfs) | ||
56 | pkg.generate(libgnunetfs, url: 'https://www.gnunet.org', | ||
57 | description : 'Provides API for GNUnet File-Sharing service') | ||
58 | |||
59 | shared_module('gnunet_plugin_block_fs', | ||
60 | ['plugin_block_fs.c'], | ||
61 | dependencies: [libgnunetutil_dep, | ||
62 | libgnunetblockgroup_dep], | ||
63 | include_directories: [incdir, configuration_inc], | ||
64 | install:true, | ||
65 | install_dir: get_option('libdir')/'gnunet') | ||
66 | |||
67 | executable ('gnunet-search', | ||
68 | 'gnunet-search.c', | ||
69 | dependencies: [libgnunetfs_dep, | ||
70 | libgnunetutil_dep], | ||
71 | include_directories: [incdir, configuration_inc], | ||
72 | install: true, | ||
73 | install_dir: get_option('bindir')) | ||
74 | executable ('gnunet-unindex', | ||
75 | 'gnunet-unindex.c', | ||
76 | dependencies: [libgnunetfs_dep, | ||
77 | libgnunetutil_dep], | ||
78 | include_directories: [incdir, configuration_inc], | ||
79 | install: true, | ||
80 | install_dir: get_option('bindir')) | ||
81 | executable ('gnunet-auto-share', | ||
82 | 'gnunet-auto-share.c', | ||
83 | dependencies: [libgnunetfs_dep, | ||
84 | libgnunetutil_dep], | ||
85 | include_directories: [incdir, configuration_inc], | ||
86 | install: true, | ||
87 | install_dir: get_option('bindir')) | ||
88 | executable ('gnunet-directory', | ||
89 | 'gnunet-directory.c', | ||
90 | dependencies: [libgnunetfs_dep, | ||
91 | libgnunetutil_dep], | ||
92 | include_directories: [incdir, configuration_inc], | ||
93 | install: true, | ||
94 | install_dir: get_option('bindir')) | ||
95 | executable ('gnunet-download', | ||
96 | 'gnunet-download.c', | ||
97 | dependencies: [libgnunetfs_dep, | ||
98 | libgnunetutil_dep], | ||
99 | include_directories: [incdir, configuration_inc], | ||
100 | install: true, | ||
101 | install_dir: get_option('bindir')) | ||
102 | executable ('gnunet-fs', | ||
103 | 'gnunet-fs.c', | ||
104 | dependencies: [libgnunetfs_dep, | ||
105 | libgnunetutil_dep], | ||
106 | include_directories: [incdir, configuration_inc], | ||
107 | install: true, | ||
108 | install_dir: get_option('bindir')) | ||
109 | executable ('gnunet-publish', | ||
110 | 'gnunet-publish.c', | ||
111 | dependencies: [libgnunetfs_dep, | ||
112 | libgnunetidentity_dep, | ||
113 | libgnunetutil_dep], | ||
114 | include_directories: [incdir, configuration_inc], | ||
115 | install: true, | ||
116 | install_dir: get_option('bindir')) | ||
117 | executable ('gnunet-service-fs', | ||
118 | gnunetservicefs_src, | ||
119 | dependencies: [libgnunetfs_dep, | ||
120 | libgnunetutil_dep, | ||
121 | libgnunetstatistics_dep, | ||
122 | libgnunetcore_dep, | ||
123 | libgnunetdht_dep, | ||
124 | libgnunetidentity_dep, | ||
125 | m_dep, | ||
126 | libgnunetcadet_dep, | ||
127 | libgnunetpeerstore_dep, | ||
128 | libgnunetdatastore_dep, | ||
129 | libgnunetblock_dep], | ||
130 | include_directories: [incdir, configuration_inc], | ||
131 | install: true, | ||
132 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
133 | executable ('gnunet-helper-fs-publish', | ||
134 | ['gnunet-helper-fs-publish.c'], | ||
135 | dependencies: [libgnunetfs_dep, | ||
136 | libgnunetutil_dep, | ||
137 | libgnunetblock_dep], | ||
138 | include_directories: [incdir, configuration_inc], | ||
139 | install: true, | ||
140 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
141 | |||
diff --git a/src/fs/meta_data.c b/src/fs/meta_data.c deleted file mode 100644 index 1e75ecf6c..000000000 --- a/src/fs/meta_data.c +++ /dev/null | |||
@@ -1,1229 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010, 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 | /** | ||
22 | * @file fs/meta_data.c | ||
23 | * @brief Storing of meta data | ||
24 | * @author Christian Grothoff | ||
25 | * @author Martin Schanzenbach | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_fs_service.h" | ||
30 | |||
31 | /** | ||
32 | * Maximum size allowed for meta data written/read from disk. | ||
33 | * File-sharing limits to 64k, so this should be rather generous. | ||
34 | */ | ||
35 | #define MAX_META_DATA (1024 * 1024) | ||
36 | |||
37 | |||
38 | #define LOG(kind, ...) GNUNET_log_from (kind, "fs-meta-data", \ | ||
39 | __VA_ARGS__) | ||
40 | |||
41 | |||
42 | |||
43 | /** | ||
44 | * Meta data item. | ||
45 | */ | ||
46 | struct MetaItem | ||
47 | { | ||
48 | /** | ||
49 | * This is a doubly linked list. | ||
50 | */ | ||
51 | struct MetaItem *next; | ||
52 | |||
53 | /** | ||
54 | * This is a doubly linked list. | ||
55 | */ | ||
56 | struct MetaItem *prev; | ||
57 | |||
58 | /** | ||
59 | * Name of the extracting plugin. | ||
60 | */ | ||
61 | char *plugin_name; | ||
62 | |||
63 | /** | ||
64 | * Mime-type of data. | ||
65 | */ | ||
66 | char *mime_type; | ||
67 | |||
68 | /** | ||
69 | * The actual meta data. | ||
70 | */ | ||
71 | char *data; | ||
72 | |||
73 | /** | ||
74 | * Number of bytes in 'data'. | ||
75 | */ | ||
76 | size_t data_size; | ||
77 | |||
78 | /** | ||
79 | * Type of the meta data. | ||
80 | */ | ||
81 | enum EXTRACTOR_MetaType type; | ||
82 | |||
83 | /** | ||
84 | * Format of the meta data. | ||
85 | */ | ||
86 | enum EXTRACTOR_MetaFormat format; | ||
87 | }; | ||
88 | |||
89 | /** | ||
90 | * Meta data to associate with a file, directory or namespace. | ||
91 | */ | ||
92 | struct GNUNET_FS_MetaData | ||
93 | { | ||
94 | /** | ||
95 | * Head of linked list of the meta data items. | ||
96 | */ | ||
97 | struct MetaItem *items_head; | ||
98 | |||
99 | /** | ||
100 | * Tail of linked list of the meta data items. | ||
101 | */ | ||
102 | struct MetaItem *items_tail; | ||
103 | |||
104 | /** | ||
105 | * Complete serialized and compressed buffer of the items. | ||
106 | * NULL if we have not computed that buffer yet. | ||
107 | */ | ||
108 | char *sbuf; | ||
109 | |||
110 | /** | ||
111 | * Number of bytes in 'sbuf'. 0 if the buffer is stale. | ||
112 | */ | ||
113 | size_t sbuf_size; | ||
114 | |||
115 | /** | ||
116 | * Number of items in the linked list. | ||
117 | */ | ||
118 | unsigned int item_count; | ||
119 | }; | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Create a fresh struct FS_MetaData token. | ||
124 | * | ||
125 | * @return empty meta-data container | ||
126 | */ | ||
127 | struct GNUNET_FS_MetaData * | ||
128 | GNUNET_FS_meta_data_create () | ||
129 | { | ||
130 | return GNUNET_new (struct GNUNET_FS_MetaData); | ||
131 | } | ||
132 | |||
133 | |||
134 | /** | ||
135 | * Free meta data item. | ||
136 | * | ||
137 | * @param mi item to free | ||
138 | */ | ||
139 | static void | ||
140 | meta_item_free (struct MetaItem *mi) | ||
141 | { | ||
142 | GNUNET_free (mi->plugin_name); | ||
143 | GNUNET_free (mi->mime_type); | ||
144 | GNUNET_free (mi->data); | ||
145 | GNUNET_free (mi); | ||
146 | } | ||
147 | |||
148 | |||
149 | /** | ||
150 | * The meta data has changed, invalidate its serialization | ||
151 | * buffer. | ||
152 | * | ||
153 | * @param md meta data that changed | ||
154 | */ | ||
155 | static void | ||
156 | invalidate_sbuf (struct GNUNET_FS_MetaData *md) | ||
157 | { | ||
158 | if (NULL == md->sbuf) | ||
159 | return; | ||
160 | GNUNET_free (md->sbuf); | ||
161 | md->sbuf = NULL; | ||
162 | md->sbuf_size = 0; | ||
163 | } | ||
164 | |||
165 | |||
166 | void | ||
167 | GNUNET_FS_meta_data_destroy (struct GNUNET_FS_MetaData *md) | ||
168 | { | ||
169 | struct MetaItem *pos; | ||
170 | |||
171 | if (NULL == md) | ||
172 | return; | ||
173 | while (NULL != (pos = md->items_head)) | ||
174 | { | ||
175 | GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos); | ||
176 | meta_item_free (pos); | ||
177 | } | ||
178 | GNUNET_free (md->sbuf); | ||
179 | GNUNET_free (md); | ||
180 | } | ||
181 | |||
182 | |||
183 | void | ||
184 | GNUNET_FS_meta_data_clear (struct GNUNET_FS_MetaData *md) | ||
185 | { | ||
186 | struct MetaItem *mi; | ||
187 | |||
188 | if (NULL == md) | ||
189 | return; | ||
190 | while (NULL != (mi = md->items_head)) | ||
191 | { | ||
192 | GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, mi); | ||
193 | meta_item_free (mi); | ||
194 | } | ||
195 | GNUNET_free (md->sbuf); | ||
196 | memset (md, 0, sizeof(struct GNUNET_FS_MetaData)); | ||
197 | } | ||
198 | |||
199 | |||
200 | int | ||
201 | GNUNET_FS_meta_data_test_equal (const struct GNUNET_FS_MetaData | ||
202 | *md1, | ||
203 | const struct GNUNET_FS_MetaData | ||
204 | *md2) | ||
205 | { | ||
206 | struct MetaItem *i; | ||
207 | struct MetaItem *j; | ||
208 | int found; | ||
209 | |||
210 | if (md1 == md2) | ||
211 | return GNUNET_YES; | ||
212 | if (md1->item_count != md2->item_count) | ||
213 | return GNUNET_NO; | ||
214 | for (i = md1->items_head; NULL != i; i = i->next) | ||
215 | { | ||
216 | found = GNUNET_NO; | ||
217 | for (j = md2->items_head; NULL != j; j = j->next) | ||
218 | { | ||
219 | if ((i->type == j->type) && (i->format == j->format) && | ||
220 | (i->data_size == j->data_size) && | ||
221 | (0 == memcmp (i->data, j->data, i->data_size))) | ||
222 | { | ||
223 | found = GNUNET_YES; | ||
224 | break; | ||
225 | } | ||
226 | if (j->data_size < i->data_size) | ||
227 | break; /* elements are sorted by (decreasing) size... */ | ||
228 | } | ||
229 | if (GNUNET_NO == found) | ||
230 | return GNUNET_NO; | ||
231 | } | ||
232 | return GNUNET_YES; | ||
233 | } | ||
234 | |||
235 | |||
236 | /** | ||
237 | * Extend metadata. Note that the list of meta data items is | ||
238 | * sorted by size (largest first). | ||
239 | * | ||
240 | * @param md metadata to extend | ||
241 | * @param plugin_name name of the plugin that produced this value; | ||
242 | * special values can be used (e.g. '<zlib>' for zlib being | ||
243 | * used in the main libextractor library and yielding | ||
244 | * meta data). | ||
245 | * @param type libextractor-type describing the meta data | ||
246 | * @param format basic format information about data | ||
247 | * @param data_mime_type mime-type of data (not of the original file); | ||
248 | * can be NULL (if mime-type is not known) | ||
249 | * @param data actual meta-data found | ||
250 | * @param data_size number of bytes in @a data | ||
251 | * @return #GNUNET_OK on success, #GNUNET_SYSERR if this entry already exists | ||
252 | * data_mime_type and plugin_name are not considered for "exists" checks | ||
253 | */ | ||
254 | int | ||
255 | GNUNET_FS_meta_data_insert (struct GNUNET_FS_MetaData *md, | ||
256 | const char *plugin_name, | ||
257 | enum EXTRACTOR_MetaType type, | ||
258 | enum EXTRACTOR_MetaFormat format, | ||
259 | const char *data_mime_type, const char *data, | ||
260 | size_t data_size) | ||
261 | { | ||
262 | struct MetaItem *pos; | ||
263 | struct MetaItem *mi; | ||
264 | char *p; | ||
265 | |||
266 | if ((EXTRACTOR_METAFORMAT_UTF8 == format) || | ||
267 | (EXTRACTOR_METAFORMAT_C_STRING == format)) | ||
268 | GNUNET_break ('\0' == data[data_size - 1]); | ||
269 | |||
270 | for (pos = md->items_head; NULL != pos; pos = pos->next) | ||
271 | { | ||
272 | if (pos->data_size < data_size) | ||
273 | break; /* elements are sorted by size in the list */ | ||
274 | if ((pos->type == type) && (pos->data_size == data_size) && | ||
275 | (0 == memcmp (pos->data, data, data_size))) | ||
276 | { | ||
277 | if ((NULL == pos->mime_type) && (NULL != data_mime_type)) | ||
278 | { | ||
279 | pos->mime_type = GNUNET_strdup (data_mime_type); | ||
280 | invalidate_sbuf (md); | ||
281 | } | ||
282 | if ((EXTRACTOR_METAFORMAT_C_STRING == pos->format) && | ||
283 | (EXTRACTOR_METAFORMAT_UTF8 == format)) | ||
284 | { | ||
285 | pos->format = EXTRACTOR_METAFORMAT_UTF8; | ||
286 | invalidate_sbuf (md); | ||
287 | } | ||
288 | return GNUNET_SYSERR; | ||
289 | } | ||
290 | } | ||
291 | md->item_count++; | ||
292 | mi = GNUNET_new (struct MetaItem); | ||
293 | mi->type = type; | ||
294 | mi->format = format; | ||
295 | mi->data_size = data_size; | ||
296 | if (NULL == pos) | ||
297 | GNUNET_CONTAINER_DLL_insert_tail (md->items_head, | ||
298 | md->items_tail, | ||
299 | mi); | ||
300 | else | ||
301 | GNUNET_CONTAINER_DLL_insert_after (md->items_head, | ||
302 | md->items_tail, | ||
303 | pos->prev, | ||
304 | mi); | ||
305 | mi->mime_type = | ||
306 | (NULL == data_mime_type) ? NULL : GNUNET_strdup (data_mime_type); | ||
307 | mi->plugin_name = (NULL == plugin_name) ? NULL : GNUNET_strdup (plugin_name); | ||
308 | mi->data = GNUNET_malloc (data_size); | ||
309 | GNUNET_memcpy (mi->data, data, data_size); | ||
310 | /* change all dir separators to POSIX style ('/') */ | ||
311 | if ((EXTRACTOR_METATYPE_FILENAME == type) || | ||
312 | (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type)) | ||
313 | { | ||
314 | p = mi->data; | ||
315 | while (('\0' != *p) && (p < mi->data + data_size)) | ||
316 | { | ||
317 | if ('\\' == *p) | ||
318 | *p = '/'; | ||
319 | p++; | ||
320 | } | ||
321 | } | ||
322 | invalidate_sbuf (md); | ||
323 | return GNUNET_OK; | ||
324 | } | ||
325 | |||
326 | |||
327 | /** | ||
328 | * Merge given meta data. | ||
329 | * | ||
330 | * @param cls the `struct GNUNET_FS_MetaData` to merge into | ||
331 | * @param plugin_name name of the plugin that produced this value; | ||
332 | * special values can be used (e.g. '<zlib>' for zlib being | ||
333 | * used in the main libextractor library and yielding | ||
334 | * meta data). | ||
335 | * @param type libextractor-type describing the meta data | ||
336 | * @param format basic format information about data | ||
337 | * @param data_mime_type mime-type of data (not of the original file); | ||
338 | * can be NULL (if mime-type is not known) | ||
339 | * @param data actual meta-data found | ||
340 | * @param data_size number of bytes in @a data | ||
341 | * @return 0 (to continue) | ||
342 | */ | ||
343 | static int | ||
344 | merge_helper (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type, | ||
345 | enum EXTRACTOR_MetaFormat format, const char *data_mime_type, | ||
346 | const char *data, size_t data_size) | ||
347 | { | ||
348 | struct GNUNET_FS_MetaData *md = cls; | ||
349 | |||
350 | (void) GNUNET_FS_meta_data_insert (md, plugin_name, type, format, | ||
351 | data_mime_type, data, data_size); | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | |||
356 | void | ||
357 | GNUNET_FS_meta_data_merge (struct GNUNET_FS_MetaData *md, | ||
358 | const struct GNUNET_FS_MetaData *in) | ||
359 | { | ||
360 | GNUNET_FS_meta_data_iterate (in, &merge_helper, md); | ||
361 | } | ||
362 | |||
363 | |||
364 | int | ||
365 | GNUNET_FS_meta_data_delete (struct GNUNET_FS_MetaData *md, | ||
366 | enum EXTRACTOR_MetaType type, | ||
367 | const char *data, size_t data_size) | ||
368 | { | ||
369 | struct MetaItem *pos; | ||
370 | |||
371 | for (pos = md->items_head; NULL != pos; pos = pos->next) | ||
372 | { | ||
373 | if (pos->data_size < data_size) | ||
374 | break; /* items are sorted by (decreasing) size */ | ||
375 | if ((pos->type == type) && | ||
376 | ((NULL == data) || | ||
377 | ((pos->data_size == data_size) && | ||
378 | (0 == memcmp (pos->data, data, data_size))))) | ||
379 | { | ||
380 | GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos); | ||
381 | meta_item_free (pos); | ||
382 | md->item_count--; | ||
383 | invalidate_sbuf (md); | ||
384 | return GNUNET_OK; | ||
385 | } | ||
386 | } | ||
387 | return GNUNET_SYSERR; | ||
388 | } | ||
389 | |||
390 | |||
391 | void | ||
392 | GNUNET_FS_meta_data_add_publication_date (struct | ||
393 | GNUNET_FS_MetaData *md) | ||
394 | { | ||
395 | const char *dat; | ||
396 | struct GNUNET_TIME_Absolute t; | ||
397 | |||
398 | t = GNUNET_TIME_absolute_get (); | ||
399 | GNUNET_FS_meta_data_delete (md, | ||
400 | EXTRACTOR_METATYPE_PUBLICATION_DATE, | ||
401 | NULL, 0); | ||
402 | dat = GNUNET_STRINGS_absolute_time_to_string (t); | ||
403 | GNUNET_FS_meta_data_insert (md, "<gnunet>", | ||
404 | EXTRACTOR_METATYPE_PUBLICATION_DATE, | ||
405 | EXTRACTOR_METAFORMAT_UTF8, "text/plain", | ||
406 | dat, strlen (dat) + 1); | ||
407 | } | ||
408 | |||
409 | |||
410 | /** | ||
411 | * Iterate over MD entries. | ||
412 | * | ||
413 | * @param md metadata to inspect | ||
414 | * @param iter function to call on each entry | ||
415 | * @param iter_cls closure for iterator | ||
416 | * @return number of entries | ||
417 | */ | ||
418 | int | ||
419 | GNUNET_FS_meta_data_iterate (const struct GNUNET_FS_MetaData *md, | ||
420 | EXTRACTOR_MetaDataProcessor iter, | ||
421 | void *iter_cls) | ||
422 | { | ||
423 | struct MetaItem *pos; | ||
424 | |||
425 | if (NULL == md) | ||
426 | return 0; | ||
427 | if (NULL == iter) | ||
428 | return md->item_count; | ||
429 | for (pos = md->items_head; NULL != pos; pos = pos->next) | ||
430 | if (0 != | ||
431 | iter (iter_cls, pos->plugin_name, pos->type, pos->format, | ||
432 | pos->mime_type, pos->data, pos->data_size)) | ||
433 | return md->item_count; | ||
434 | return md->item_count; | ||
435 | } | ||
436 | |||
437 | |||
438 | char * | ||
439 | GNUNET_FS_meta_data_get_by_type (const struct | ||
440 | GNUNET_FS_MetaData *md, | ||
441 | enum EXTRACTOR_MetaType type) | ||
442 | { | ||
443 | struct MetaItem *pos; | ||
444 | |||
445 | if (NULL == md) | ||
446 | return NULL; | ||
447 | for (pos = md->items_head; NULL != pos; pos = pos->next) | ||
448 | if ((type == pos->type) && | ||
449 | ((pos->format == EXTRACTOR_METAFORMAT_UTF8) || | ||
450 | (pos->format == EXTRACTOR_METAFORMAT_C_STRING))) | ||
451 | return GNUNET_strdup (pos->data); | ||
452 | return NULL; | ||
453 | } | ||
454 | |||
455 | |||
456 | char * | ||
457 | GNUNET_FS_meta_data_get_first_by_types (const struct | ||
458 | GNUNET_FS_MetaData *md, | ||
459 | ...) | ||
460 | { | ||
461 | char *ret; | ||
462 | va_list args; | ||
463 | int type; | ||
464 | |||
465 | if (NULL == md) | ||
466 | return NULL; | ||
467 | ret = NULL; | ||
468 | va_start (args, md); | ||
469 | while (1) | ||
470 | { | ||
471 | type = va_arg (args, int); | ||
472 | if (-1 == type) | ||
473 | break; | ||
474 | if (NULL != (ret = GNUNET_FS_meta_data_get_by_type (md, type))) | ||
475 | break; | ||
476 | } | ||
477 | va_end (args); | ||
478 | return ret; | ||
479 | } | ||
480 | |||
481 | |||
482 | /** | ||
483 | * Get a thumbnail from the meta-data (if present). | ||
484 | * | ||
485 | * @param md metadata to get the thumbnail from | ||
486 | * @param thumb will be set to the thumbnail data. Must be | ||
487 | * freed by the caller! | ||
488 | * @return number of bytes in thumbnail, 0 if not available | ||
489 | */ | ||
490 | size_t | ||
491 | GNUNET_FS_meta_data_get_thumbnail (const struct GNUNET_FS_MetaData | ||
492 | *md, unsigned char **thumb) | ||
493 | { | ||
494 | struct MetaItem *pos; | ||
495 | struct MetaItem *match; | ||
496 | |||
497 | if (NULL == md) | ||
498 | return 0; | ||
499 | match = NULL; | ||
500 | for (pos = md->items_head; NULL != pos; pos = pos->next) | ||
501 | { | ||
502 | if ((NULL != pos->mime_type) && | ||
503 | (0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) && | ||
504 | (EXTRACTOR_METAFORMAT_BINARY == pos->format)) | ||
505 | { | ||
506 | if (NULL == match) | ||
507 | match = pos; | ||
508 | else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) && | ||
509 | (pos->type == EXTRACTOR_METATYPE_THUMBNAIL)) | ||
510 | match = pos; | ||
511 | } | ||
512 | } | ||
513 | if ((NULL == match) || (0 == match->data_size)) | ||
514 | return 0; | ||
515 | *thumb = GNUNET_malloc (match->data_size); | ||
516 | GNUNET_memcpy (*thumb, match->data, match->data_size); | ||
517 | return match->data_size; | ||
518 | } | ||
519 | |||
520 | |||
521 | /** | ||
522 | * Duplicate a `struct GNUNET_FS_MetaData`. | ||
523 | * | ||
524 | * @param md what to duplicate | ||
525 | * @return duplicate meta-data container | ||
526 | */ | ||
527 | struct GNUNET_FS_MetaData * | ||
528 | GNUNET_FS_meta_data_duplicate (const struct GNUNET_FS_MetaData | ||
529 | *md) | ||
530 | { | ||
531 | struct GNUNET_FS_MetaData *ret; | ||
532 | struct MetaItem *pos; | ||
533 | |||
534 | if (NULL == md) | ||
535 | return NULL; | ||
536 | ret = GNUNET_FS_meta_data_create (); | ||
537 | for (pos = md->items_tail; NULL != pos; pos = pos->prev) | ||
538 | GNUNET_FS_meta_data_insert (ret, pos->plugin_name, pos->type, | ||
539 | pos->format, pos->mime_type, pos->data, | ||
540 | pos->data_size); | ||
541 | return ret; | ||
542 | } | ||
543 | |||
544 | |||
545 | /** | ||
546 | * Flag in 'version' that indicates compressed meta-data. | ||
547 | */ | ||
548 | #define HEADER_COMPRESSED 0x80000000 | ||
549 | |||
550 | |||
551 | /** | ||
552 | * Bits in 'version' that give the version number. | ||
553 | */ | ||
554 | #define HEADER_VERSION_MASK 0x7FFFFFFF | ||
555 | |||
556 | |||
557 | /** | ||
558 | * Header for serialized meta data. | ||
559 | */ | ||
560 | struct MetaDataHeader | ||
561 | { | ||
562 | /** | ||
563 | * The version of the MD serialization. The highest bit is used to | ||
564 | * indicate compression. | ||
565 | * | ||
566 | * Version 0 is traditional (pre-0.9) meta data (unsupported) | ||
567 | * Version is 1 for a NULL pointer | ||
568 | * Version 2 is for 0.9.x (and possibly higher) | ||
569 | * Other version numbers are not yet defined. | ||
570 | */ | ||
571 | uint32_t version; | ||
572 | |||
573 | /** | ||
574 | * How many MD entries are there? | ||
575 | */ | ||
576 | uint32_t entries; | ||
577 | |||
578 | /** | ||
579 | * Size of the decompressed meta data. | ||
580 | */ | ||
581 | uint32_t size; | ||
582 | |||
583 | /** | ||
584 | * This is followed by 'entries' values of type 'struct MetaDataEntry' | ||
585 | * and then by 'entry' plugin names, mime-types and data blocks | ||
586 | * as specified in those meta data entries. | ||
587 | */ | ||
588 | }; | ||
589 | |||
590 | |||
591 | /** | ||
592 | * Entry of serialized meta data. | ||
593 | */ | ||
594 | struct MetaDataEntry | ||
595 | { | ||
596 | /** | ||
597 | * Meta data type. Corresponds to an 'enum EXTRACTOR_MetaType' | ||
598 | */ | ||
599 | uint32_t type; | ||
600 | |||
601 | /** | ||
602 | * Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat' | ||
603 | */ | ||
604 | uint32_t format; | ||
605 | |||
606 | /** | ||
607 | * Number of bytes of meta data. | ||
608 | */ | ||
609 | uint32_t data_size; | ||
610 | |||
611 | /** | ||
612 | * Number of bytes in the plugin name including 0-terminator. 0 for NULL. | ||
613 | */ | ||
614 | uint32_t plugin_name_len; | ||
615 | |||
616 | /** | ||
617 | * Number of bytes in the mime type including 0-terminator. 0 for NULL. | ||
618 | */ | ||
619 | uint32_t mime_type_len; | ||
620 | }; | ||
621 | |||
622 | |||
623 | /** | ||
624 | * Serialize meta-data to target. | ||
625 | * | ||
626 | * @param md metadata to serialize | ||
627 | * @param target where to write the serialized metadata; | ||
628 | * *target can be NULL, in which case memory is allocated | ||
629 | * @param max maximum number of bytes available in target | ||
630 | * @param opt is it ok to just write SOME of the | ||
631 | * meta-data to match the size constraint, | ||
632 | * possibly discarding some data? | ||
633 | * @return number of bytes written on success, | ||
634 | * #GNUNET_SYSERR on error (typically: not enough | ||
635 | * space) | ||
636 | */ | ||
637 | ssize_t | ||
638 | GNUNET_FS_meta_data_serialize (const struct GNUNET_FS_MetaData | ||
639 | *md, char **target, size_t max, | ||
640 | enum | ||
641 | GNUNET_FS_MetaDataSerializationOptions | ||
642 | opt) | ||
643 | { | ||
644 | struct GNUNET_FS_MetaData *vmd; | ||
645 | struct MetaItem *pos; | ||
646 | struct MetaDataHeader ihdr; | ||
647 | struct MetaDataHeader *hdr; | ||
648 | struct MetaDataEntry *ent; | ||
649 | char *dst; | ||
650 | unsigned int i; | ||
651 | uint64_t msize; | ||
652 | size_t off; | ||
653 | char *mdata; | ||
654 | char *cdata; | ||
655 | size_t mlen; | ||
656 | size_t plen; | ||
657 | size_t size; | ||
658 | size_t left; | ||
659 | size_t clen; | ||
660 | size_t rlen; | ||
661 | int comp; | ||
662 | |||
663 | if (max < sizeof(struct MetaDataHeader)) | ||
664 | return GNUNET_SYSERR; /* far too small */ | ||
665 | if (NULL == md) | ||
666 | return 0; | ||
667 | |||
668 | if (NULL != md->sbuf) | ||
669 | { | ||
670 | /* try to use serialization cache */ | ||
671 | if (md->sbuf_size <= max) | ||
672 | { | ||
673 | if (NULL == *target) | ||
674 | *target = GNUNET_malloc (md->sbuf_size); | ||
675 | GNUNET_memcpy (*target, md->sbuf, md->sbuf_size); | ||
676 | return md->sbuf_size; | ||
677 | } | ||
678 | if (0 == (opt & GNUNET_FS_META_DATA_SERIALIZE_PART)) | ||
679 | return GNUNET_SYSERR; /* can say that this will fail */ | ||
680 | /* need to compute a partial serialization, sbuf useless ... */ | ||
681 | } | ||
682 | dst = NULL; | ||
683 | msize = 0; | ||
684 | for (pos = md->items_tail; NULL != pos; pos = pos->prev) | ||
685 | { | ||
686 | msize += sizeof(struct MetaDataEntry); | ||
687 | msize += pos->data_size; | ||
688 | if (NULL != pos->plugin_name) | ||
689 | msize += strlen (pos->plugin_name) + 1; | ||
690 | if (NULL != pos->mime_type) | ||
691 | msize += strlen (pos->mime_type) + 1; | ||
692 | } | ||
693 | size = (size_t) msize; | ||
694 | if (size != msize) | ||
695 | { | ||
696 | GNUNET_break (0); /* integer overflow */ | ||
697 | return GNUNET_SYSERR; | ||
698 | } | ||
699 | if (size >= GNUNET_MAX_MALLOC_CHECKED) | ||
700 | { | ||
701 | /* too large to be processed */ | ||
702 | return GNUNET_SYSERR; | ||
703 | } | ||
704 | ent = GNUNET_malloc (size); | ||
705 | mdata = (char *) &ent[md->item_count]; | ||
706 | off = size - (md->item_count * sizeof(struct MetaDataEntry)); | ||
707 | i = 0; | ||
708 | for (pos = md->items_head; NULL != pos; pos = pos->next) | ||
709 | { | ||
710 | ent[i].type = htonl ((uint32_t) pos->type); | ||
711 | ent[i].format = htonl ((uint32_t) pos->format); | ||
712 | ent[i].data_size = htonl ((uint32_t) pos->data_size); | ||
713 | if (NULL == pos->plugin_name) | ||
714 | plen = 0; | ||
715 | else | ||
716 | plen = strlen (pos->plugin_name) + 1; | ||
717 | ent[i].plugin_name_len = htonl ((uint32_t) plen); | ||
718 | if (NULL == pos->mime_type) | ||
719 | mlen = 0; | ||
720 | else | ||
721 | mlen = strlen (pos->mime_type) + 1; | ||
722 | ent[i].mime_type_len = htonl ((uint32_t) mlen); | ||
723 | off -= pos->data_size; | ||
724 | if ((EXTRACTOR_METAFORMAT_UTF8 == pos->format) || | ||
725 | (EXTRACTOR_METAFORMAT_C_STRING == pos->format)) | ||
726 | GNUNET_break ('\0' == pos->data[pos->data_size - 1]); | ||
727 | GNUNET_memcpy (&mdata[off], pos->data, pos->data_size); | ||
728 | off -= plen; | ||
729 | if (NULL != pos->plugin_name) | ||
730 | GNUNET_memcpy (&mdata[off], pos->plugin_name, plen); | ||
731 | off -= mlen; | ||
732 | if (NULL != pos->mime_type) | ||
733 | GNUNET_memcpy (&mdata[off], pos->mime_type, mlen); | ||
734 | i++; | ||
735 | } | ||
736 | GNUNET_assert (0 == off); | ||
737 | |||
738 | clen = 0; | ||
739 | cdata = NULL; | ||
740 | left = size; | ||
741 | i = 0; | ||
742 | for (pos = md->items_head; NULL != pos; pos = pos->next) | ||
743 | { | ||
744 | comp = GNUNET_NO; | ||
745 | if (0 == (opt & GNUNET_FS_META_DATA_SERIALIZE_NO_COMPRESS)) | ||
746 | comp = GNUNET_try_compression ((const char *) &ent[i], | ||
747 | left, | ||
748 | &cdata, | ||
749 | &clen); | ||
750 | |||
751 | if ((NULL == md->sbuf) && (0 == i)) | ||
752 | { | ||
753 | /* fill 'sbuf'; this "modifies" md, but since this is only | ||
754 | * an internal cache we will cast away the 'const' instead | ||
755 | * of making the API look strange. */ | ||
756 | vmd = (struct GNUNET_FS_MetaData *) md; | ||
757 | hdr = GNUNET_malloc (left + sizeof(struct MetaDataHeader)); | ||
758 | hdr->size = htonl (left); | ||
759 | hdr->entries = htonl (md->item_count); | ||
760 | if (GNUNET_YES == comp) | ||
761 | { | ||
762 | GNUNET_assert (clen < left); | ||
763 | hdr->version = htonl (2 | HEADER_COMPRESSED); | ||
764 | GNUNET_memcpy (&hdr[1], cdata, clen); | ||
765 | vmd->sbuf_size = clen + sizeof(struct MetaDataHeader); | ||
766 | } | ||
767 | else | ||
768 | { | ||
769 | hdr->version = htonl (2); | ||
770 | GNUNET_memcpy (&hdr[1], &ent[0], left); | ||
771 | vmd->sbuf_size = left + sizeof(struct MetaDataHeader); | ||
772 | } | ||
773 | vmd->sbuf = (char *) hdr; | ||
774 | } | ||
775 | |||
776 | if (((left + sizeof(struct MetaDataHeader)) <= max) || | ||
777 | ((GNUNET_YES == comp) && (clen <= max))) | ||
778 | { | ||
779 | /* success, this now fits! */ | ||
780 | if (GNUNET_YES == comp) | ||
781 | { | ||
782 | if (NULL == dst) | ||
783 | dst = GNUNET_malloc (clen + sizeof(struct MetaDataHeader)); | ||
784 | hdr = (struct MetaDataHeader *) dst; | ||
785 | hdr->version = htonl (2 | HEADER_COMPRESSED); | ||
786 | hdr->size = htonl (left); | ||
787 | hdr->entries = htonl (md->item_count - i); | ||
788 | GNUNET_memcpy (&dst[sizeof(struct MetaDataHeader)], cdata, clen); | ||
789 | GNUNET_free (cdata); | ||
790 | cdata = NULL; | ||
791 | GNUNET_free (ent); | ||
792 | rlen = clen + sizeof(struct MetaDataHeader); | ||
793 | } | ||
794 | else | ||
795 | { | ||
796 | if (NULL == dst) | ||
797 | dst = GNUNET_malloc (left + sizeof(struct MetaDataHeader)); | ||
798 | hdr = (struct MetaDataHeader *) dst; | ||
799 | hdr->version = htonl (2); | ||
800 | hdr->entries = htonl (md->item_count - i); | ||
801 | hdr->size = htonl (left); | ||
802 | GNUNET_memcpy (&dst[sizeof(struct MetaDataHeader)], &ent[i], left); | ||
803 | GNUNET_free (ent); | ||
804 | rlen = left + sizeof(struct MetaDataHeader); | ||
805 | } | ||
806 | if (NULL != *target) | ||
807 | { | ||
808 | if (GNUNET_YES == comp) | ||
809 | GNUNET_memcpy (*target, dst, clen + sizeof(struct MetaDataHeader)); | ||
810 | else | ||
811 | GNUNET_memcpy (*target, dst, left + sizeof(struct MetaDataHeader)); | ||
812 | GNUNET_free (dst); | ||
813 | } | ||
814 | else | ||
815 | { | ||
816 | *target = dst; | ||
817 | } | ||
818 | return rlen; | ||
819 | } | ||
820 | |||
821 | if (0 == (opt & GNUNET_FS_META_DATA_SERIALIZE_PART)) | ||
822 | { | ||
823 | /* does not fit! */ | ||
824 | GNUNET_free (ent); | ||
825 | if (NULL != cdata) | ||
826 | GNUNET_free (cdata); | ||
827 | cdata = NULL; | ||
828 | return GNUNET_SYSERR; | ||
829 | } | ||
830 | |||
831 | /* next iteration: ignore the corresponding meta data at the | ||
832 | * end and try again without it */ | ||
833 | left -= sizeof(struct MetaDataEntry); | ||
834 | left -= pos->data_size; | ||
835 | if (NULL != pos->plugin_name) | ||
836 | left -= strlen (pos->plugin_name) + 1; | ||
837 | if (NULL != pos->mime_type) | ||
838 | left -= strlen (pos->mime_type) + 1; | ||
839 | |||
840 | if (NULL != cdata) | ||
841 | GNUNET_free (cdata); | ||
842 | cdata = NULL; | ||
843 | i++; | ||
844 | } | ||
845 | GNUNET_free (ent); | ||
846 | |||
847 | /* nothing fit, only write header! */ | ||
848 | ihdr.version = htonl (2); | ||
849 | ihdr.entries = htonl (0); | ||
850 | ihdr.size = htonl (0); | ||
851 | if (NULL == *target) | ||
852 | *target = (char *) GNUNET_new (struct MetaDataHeader); | ||
853 | GNUNET_memcpy (*target, &ihdr, sizeof(struct MetaDataHeader)); | ||
854 | return sizeof(struct MetaDataHeader); | ||
855 | } | ||
856 | |||
857 | |||
858 | ssize_t | ||
859 | GNUNET_FS_meta_data_get_serialized_size (const struct | ||
860 | GNUNET_FS_MetaData *md) | ||
861 | { | ||
862 | ssize_t ret; | ||
863 | char *ptr; | ||
864 | |||
865 | if (NULL != md->sbuf) | ||
866 | return md->sbuf_size; | ||
867 | ptr = NULL; | ||
868 | ret = | ||
869 | GNUNET_FS_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED, | ||
870 | GNUNET_FS_META_DATA_SERIALIZE_FULL); | ||
871 | if (-1 != ret) | ||
872 | GNUNET_free (ptr); | ||
873 | return ret; | ||
874 | } | ||
875 | |||
876 | |||
877 | /** | ||
878 | * Deserialize meta-data. Initializes md. | ||
879 | * | ||
880 | * @param input buffer with the serialized metadata | ||
881 | * @param size number of bytes available in input | ||
882 | * @return MD on success, NULL on error (i.e. | ||
883 | * bad format) | ||
884 | */ | ||
885 | struct GNUNET_FS_MetaData * | ||
886 | GNUNET_FS_meta_data_deserialize (const char *input, size_t size) | ||
887 | { | ||
888 | struct GNUNET_FS_MetaData *md; | ||
889 | struct MetaDataHeader hdr; | ||
890 | struct MetaDataEntry ent; | ||
891 | uint32_t ic; | ||
892 | uint32_t i; | ||
893 | char *data; | ||
894 | const char *cdata; | ||
895 | uint32_t version; | ||
896 | uint32_t dataSize; | ||
897 | int compressed; | ||
898 | size_t left; | ||
899 | uint32_t mlen; | ||
900 | uint32_t plen; | ||
901 | uint32_t dlen; | ||
902 | const char *mdata; | ||
903 | const char *meta_data; | ||
904 | const char *plugin_name; | ||
905 | const char *mime_type; | ||
906 | enum EXTRACTOR_MetaFormat format; | ||
907 | |||
908 | if (size < sizeof(struct MetaDataHeader)) | ||
909 | return NULL; | ||
910 | GNUNET_memcpy (&hdr, input, sizeof(struct MetaDataHeader)); | ||
911 | version = ntohl (hdr.version) & HEADER_VERSION_MASK; | ||
912 | compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0; | ||
913 | |||
914 | if (1 == version) | ||
915 | return NULL; /* null pointer */ | ||
916 | if (2 != version) | ||
917 | { | ||
918 | GNUNET_break_op (0); /* unsupported version */ | ||
919 | return NULL; | ||
920 | } | ||
921 | |||
922 | ic = ntohl (hdr.entries); | ||
923 | dataSize = ntohl (hdr.size); | ||
924 | if (((sizeof(struct MetaDataEntry) * ic) > dataSize) || | ||
925 | ((0 != ic) && | ||
926 | (dataSize / ic < sizeof(struct MetaDataEntry)))) | ||
927 | { | ||
928 | GNUNET_break_op (0); | ||
929 | return NULL; | ||
930 | } | ||
931 | |||
932 | if (compressed) | ||
933 | { | ||
934 | if (dataSize >= GNUNET_MAX_MALLOC_CHECKED) | ||
935 | { | ||
936 | /* make sure we don't blow our memory limit because of a mal-formed | ||
937 | * message... */ | ||
938 | GNUNET_break_op (0); | ||
939 | return NULL; | ||
940 | } | ||
941 | data = | ||
942 | GNUNET_decompress ((const char *) &input[sizeof(struct MetaDataHeader)], | ||
943 | size - sizeof(struct MetaDataHeader), | ||
944 | dataSize); | ||
945 | if (NULL == data) | ||
946 | { | ||
947 | GNUNET_break_op (0); | ||
948 | return NULL; | ||
949 | } | ||
950 | cdata = data; | ||
951 | } | ||
952 | else | ||
953 | { | ||
954 | data = NULL; | ||
955 | cdata = (const char *) &input[sizeof(struct MetaDataHeader)]; | ||
956 | if (dataSize != size - sizeof(struct MetaDataHeader)) | ||
957 | { | ||
958 | GNUNET_break_op (0); | ||
959 | return NULL; | ||
960 | } | ||
961 | } | ||
962 | |||
963 | md = GNUNET_FS_meta_data_create (); | ||
964 | left = dataSize - ic * sizeof(struct MetaDataEntry); | ||
965 | mdata = &cdata[ic * sizeof(struct MetaDataEntry)]; | ||
966 | for (i = 0; i < ic; i++) | ||
967 | { | ||
968 | GNUNET_memcpy (&ent, &cdata[i * sizeof(struct MetaDataEntry)], | ||
969 | sizeof(struct MetaDataEntry)); | ||
970 | format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format); | ||
971 | if ((EXTRACTOR_METAFORMAT_UTF8 != format) && | ||
972 | (EXTRACTOR_METAFORMAT_C_STRING != format) && | ||
973 | (EXTRACTOR_METAFORMAT_BINARY != format)) | ||
974 | { | ||
975 | GNUNET_break_op (0); | ||
976 | break; | ||
977 | } | ||
978 | dlen = ntohl (ent.data_size); | ||
979 | plen = ntohl (ent.plugin_name_len); | ||
980 | mlen = ntohl (ent.mime_type_len); | ||
981 | if (dlen > left) | ||
982 | { | ||
983 | GNUNET_break_op (0); | ||
984 | break; | ||
985 | } | ||
986 | left -= dlen; | ||
987 | meta_data = &mdata[left]; | ||
988 | if ((EXTRACTOR_METAFORMAT_UTF8 == format) || | ||
989 | (EXTRACTOR_METAFORMAT_C_STRING == format)) | ||
990 | { | ||
991 | if (0 == dlen) | ||
992 | { | ||
993 | GNUNET_break_op (0); | ||
994 | break; | ||
995 | } | ||
996 | if ('\0' != meta_data[dlen - 1]) | ||
997 | { | ||
998 | GNUNET_break_op (0); | ||
999 | break; | ||
1000 | } | ||
1001 | } | ||
1002 | if (plen > left) | ||
1003 | { | ||
1004 | GNUNET_break_op (0); | ||
1005 | break; | ||
1006 | } | ||
1007 | left -= plen; | ||
1008 | if ((plen > 0) && ('\0' != mdata[left + plen - 1])) | ||
1009 | { | ||
1010 | GNUNET_break_op (0); | ||
1011 | break; | ||
1012 | } | ||
1013 | if (0 == plen) | ||
1014 | plugin_name = NULL; | ||
1015 | else | ||
1016 | plugin_name = &mdata[left]; | ||
1017 | |||
1018 | if (mlen > left) | ||
1019 | { | ||
1020 | GNUNET_break_op (0); | ||
1021 | break; | ||
1022 | } | ||
1023 | left -= mlen; | ||
1024 | if ((mlen > 0) && ('\0' != mdata[left + mlen - 1])) | ||
1025 | { | ||
1026 | GNUNET_break_op (0); | ||
1027 | break; | ||
1028 | } | ||
1029 | if (0 == mlen) | ||
1030 | mime_type = NULL; | ||
1031 | else | ||
1032 | mime_type = &mdata[left]; | ||
1033 | GNUNET_FS_meta_data_insert (md, plugin_name, | ||
1034 | (enum EXTRACTOR_MetaType) | ||
1035 | ntohl (ent.type), format, mime_type, | ||
1036 | meta_data, dlen); | ||
1037 | } | ||
1038 | GNUNET_free (data); | ||
1039 | return md; | ||
1040 | } | ||
1041 | |||
1042 | /** | ||
1043 | * Read a metadata container. | ||
1044 | * | ||
1045 | * @param h handle to an open file | ||
1046 | * @param what describes what is being read (for error message creation) | ||
1047 | * @param result the buffer to store a pointer to the (allocated) metadata | ||
1048 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
1049 | */ | ||
1050 | enum GNUNET_GenericReturnValue | ||
1051 | GNUNET_FS_read_meta_data (struct GNUNET_BIO_ReadHandle *h, | ||
1052 | const char *what, | ||
1053 | struct GNUNET_FS_MetaData **result) | ||
1054 | { | ||
1055 | uint32_t size; | ||
1056 | char *buf; | ||
1057 | char *emsg; | ||
1058 | struct GNUNET_FS_MetaData *meta; | ||
1059 | |||
1060 | if (GNUNET_OK != GNUNET_BIO_read_int32 (h, | ||
1061 | _ ("metadata length"), | ||
1062 | (int32_t *) &size)) | ||
1063 | return GNUNET_SYSERR; | ||
1064 | if (0 == size) | ||
1065 | { | ||
1066 | *result = NULL; | ||
1067 | return GNUNET_OK; | ||
1068 | } | ||
1069 | if (MAX_META_DATA < size) | ||
1070 | { | ||
1071 | GNUNET_asprintf (&emsg, | ||
1072 | _ ( | ||
1073 | "Serialized metadata `%s' larger than allowed (%u > %u)\n"), | ||
1074 | what, | ||
1075 | size, | ||
1076 | MAX_META_DATA); | ||
1077 | GNUNET_BIO_read_set_error (h, emsg); | ||
1078 | GNUNET_free (emsg); | ||
1079 | return GNUNET_SYSERR; | ||
1080 | } | ||
1081 | buf = GNUNET_malloc (size); | ||
1082 | if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, size)) | ||
1083 | { | ||
1084 | GNUNET_free (buf); | ||
1085 | return GNUNET_SYSERR; | ||
1086 | } | ||
1087 | meta = GNUNET_FS_meta_data_deserialize (buf, size); | ||
1088 | if (NULL == meta) | ||
1089 | { | ||
1090 | GNUNET_free (buf); | ||
1091 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1092 | _ ("Failed to deserialize metadata `%s'"), what); | ||
1093 | return GNUNET_SYSERR; | ||
1094 | } | ||
1095 | GNUNET_free (buf); | ||
1096 | *result = meta; | ||
1097 | return GNUNET_OK; | ||
1098 | } | ||
1099 | |||
1100 | /** | ||
1101 | * Write a metadata container. | ||
1102 | * | ||
1103 | * @param h the IO handle to write to | ||
1104 | * @param what what is being written (for error message creation) | ||
1105 | * @param m metadata to write | ||
1106 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
1107 | */ | ||
1108 | enum GNUNET_GenericReturnValue | ||
1109 | GNUNET_FS_write_meta_data (struct GNUNET_BIO_WriteHandle *h, | ||
1110 | const char *what, | ||
1111 | const struct GNUNET_FS_MetaData *m) | ||
1112 | { | ||
1113 | ssize_t size; | ||
1114 | char *buf; | ||
1115 | |||
1116 | if (m == NULL) | ||
1117 | return GNUNET_BIO_write_int32 (h, _ ("metadata length"), 0); | ||
1118 | buf = NULL; | ||
1119 | size = GNUNET_FS_meta_data_serialize (m, | ||
1120 | &buf, | ||
1121 | MAX_META_DATA, | ||
1122 | GNUNET_FS_META_DATA_SERIALIZE_PART); | ||
1123 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1124 | _ ("Serialized %ld bytes of metadata"), | ||
1125 | size); | ||
1126 | |||
1127 | if (-1 == size) | ||
1128 | { | ||
1129 | GNUNET_free (buf); | ||
1130 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1131 | _ ("Failed to serialize metadata `%s'"), | ||
1132 | what); | ||
1133 | return GNUNET_SYSERR; | ||
1134 | } | ||
1135 | if ((GNUNET_OK != GNUNET_BIO_write_int32 (h, | ||
1136 | _ ("metadata length"), | ||
1137 | (uint32_t) size)) | ||
1138 | || (GNUNET_OK != GNUNET_BIO_write (h, what, buf, size))) | ||
1139 | { | ||
1140 | GNUNET_free (buf); | ||
1141 | return GNUNET_SYSERR; | ||
1142 | } | ||
1143 | GNUNET_free (buf); | ||
1144 | return GNUNET_OK; | ||
1145 | } | ||
1146 | |||
1147 | /** | ||
1148 | * Function used internally to read a metadata container from within a read | ||
1149 | * spec. | ||
1150 | * | ||
1151 | * @param cls ignored, always NULL | ||
1152 | * @param h the IO handle to read from | ||
1153 | * @param what what is being read (for error message creation) | ||
1154 | * @param target where to store the data | ||
1155 | * @param target_size ignored | ||
1156 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
1157 | */ | ||
1158 | static int | ||
1159 | read_spec_handler_meta_data (void *cls, | ||
1160 | struct GNUNET_BIO_ReadHandle *h, | ||
1161 | const char *what, | ||
1162 | void *target, | ||
1163 | size_t target_size) | ||
1164 | { | ||
1165 | struct GNUNET_FS_MetaData **result = target; | ||
1166 | return GNUNET_FS_read_meta_data (h, what, result); | ||
1167 | } | ||
1168 | |||
1169 | /** | ||
1170 | * Create the specification to read a metadata container. | ||
1171 | * | ||
1172 | * @param what describes what is being read (for error message creation) | ||
1173 | * @param result the buffer to store a pointer to the (allocated) metadata | ||
1174 | * @return the read spec | ||
1175 | */ | ||
1176 | struct GNUNET_BIO_ReadSpec | ||
1177 | GNUNET_FS_read_spec_meta_data (const char *what, | ||
1178 | struct GNUNET_FS_MetaData **result) | ||
1179 | { | ||
1180 | struct GNUNET_BIO_ReadSpec rs = { | ||
1181 | .rh = &read_spec_handler_meta_data, | ||
1182 | .cls = NULL, | ||
1183 | .target = result, | ||
1184 | .size = 0, | ||
1185 | }; | ||
1186 | |||
1187 | return rs; | ||
1188 | } | ||
1189 | |||
1190 | /** | ||
1191 | * Function used internally to write a metadata container from within a write | ||
1192 | * spec. | ||
1193 | * | ||
1194 | * @param cls ignored, always NULL | ||
1195 | * @param h the IO handle to write to | ||
1196 | * @param what what is being written (for error message creation) | ||
1197 | * @param source the data to write | ||
1198 | * @param source_size ignored | ||
1199 | * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise | ||
1200 | */ | ||
1201 | static int | ||
1202 | write_spec_handler_meta_data (void *cls, | ||
1203 | struct GNUNET_BIO_WriteHandle *h, | ||
1204 | const char *what, | ||
1205 | void *source, | ||
1206 | size_t source_size) | ||
1207 | { | ||
1208 | const struct GNUNET_FS_MetaData *m = source; | ||
1209 | return GNUNET_FS_write_meta_data (h, what, m); | ||
1210 | } | ||
1211 | |||
1212 | |||
1213 | struct GNUNET_BIO_WriteSpec | ||
1214 | GNUNET_FS_write_spec_meta_data (const char *what, | ||
1215 | const struct GNUNET_FS_MetaData *m) | ||
1216 | { | ||
1217 | struct GNUNET_BIO_WriteSpec ws = { | ||
1218 | .wh = &write_spec_handler_meta_data, | ||
1219 | .cls = NULL, | ||
1220 | .what = what, | ||
1221 | .source = (void *) m, | ||
1222 | .source_size = 0, | ||
1223 | }; | ||
1224 | |||
1225 | return ws; | ||
1226 | } | ||
1227 | |||
1228 | |||
1229 | /* end of meta_data.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 2c7830f5f..000000000 --- a/src/fs/perf_gnunet_service_fs_p2p.c +++ /dev/null | |||
@@ -1,370 +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 | |||
46 | static struct GNUNET_TESTBED_Peer *daemons[NUM_DAEMONS]; | ||
47 | |||
48 | static int ok; | ||
49 | |||
50 | static struct GNUNET_TIME_Absolute start_time; | ||
51 | |||
52 | static const char *progname; | ||
53 | |||
54 | static struct GNUNET_TIME_Absolute start_time; | ||
55 | |||
56 | |||
57 | /** | ||
58 | * Master context for 'stat_run'. | ||
59 | */ | ||
60 | struct StatMaster | ||
61 | { | ||
62 | struct GNUNET_STATISTICS_Handle *stat; | ||
63 | struct GNUNET_TESTBED_Operation *op; | ||
64 | unsigned int daemon; | ||
65 | unsigned int value; | ||
66 | }; | ||
67 | |||
68 | struct StatValues | ||
69 | { | ||
70 | const char *subsystem; | ||
71 | const char *name; | ||
72 | }; | ||
73 | |||
74 | /** | ||
75 | * Statistics we print out. | ||
76 | */ | ||
77 | static 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 | */ | ||
113 | static int | ||
114 | print_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 | */ | ||
132 | static void | ||
133 | stat_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 | */ | ||
142 | static void | ||
143 | get_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 | */ | ||
163 | static void * | ||
164 | statistics_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 | */ | ||
179 | static void | ||
180 | statistics_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 | */ | ||
190 | static void | ||
191 | stat_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 | |||
240 | static void | ||
241 | do_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 | |||
289 | static void | ||
290 | do_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 | |||
323 | static void | ||
324 | do_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 | |||
354 | int | ||
355 | main (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_purge_cfg_dir | ||
364 | ("perf_gnunet_service_fs_p2p.conf", | ||
365 | "GNUNET_TEST_HOME"); | ||
366 | return ok; | ||
367 | } | ||
368 | |||
369 | |||
370 | /* 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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-fs-test-lib/ | ||
4 | |||
5 | [fs] | ||
6 | GAUGER_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 6b71b1f93..000000000 --- a/src/fs/perf_gnunet_service_fs_p2p_respect.c +++ /dev/null | |||
@@ -1,480 +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 | |||
77 | static struct GNUNET_TESTBED_Peer *daemons[NUM_DAEMONS]; | ||
78 | |||
79 | static int ok; | ||
80 | |||
81 | static struct GNUNET_TIME_Absolute start_time; | ||
82 | |||
83 | static const char *progname; | ||
84 | |||
85 | static struct GNUNET_FS_Uri *uri1; | ||
86 | |||
87 | static struct GNUNET_FS_Uri *uri2; | ||
88 | |||
89 | static char *fn1; | ||
90 | |||
91 | static char *fn2; | ||
92 | |||
93 | /** | ||
94 | * Master context for 'stat_run'. | ||
95 | */ | ||
96 | struct StatMaster | ||
97 | { | ||
98 | struct GNUNET_STATISTICS_Handle *stat; | ||
99 | struct GNUNET_TESTBED_Operation *op; | ||
100 | unsigned int daemon; | ||
101 | unsigned int value; | ||
102 | }; | ||
103 | |||
104 | struct StatValues | ||
105 | { | ||
106 | const char *subsystem; | ||
107 | const char *name; | ||
108 | }; | ||
109 | |||
110 | /** | ||
111 | * Statistics we print out. | ||
112 | */ | ||
113 | static 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 | |||
138 | static void | ||
139 | cleanup () | ||
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 | */ | ||
165 | static int | ||
166 | print_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 | */ | ||
180 | static void | ||
181 | stat_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 | */ | ||
190 | static void | ||
191 | get_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 | */ | ||
211 | static void * | ||
212 | statistics_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 | */ | ||
227 | static void | ||
228 | statistics_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 | */ | ||
238 | static void | ||
239 | stat_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 | |||
280 | static void | ||
281 | do_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 | |||
325 | static void | ||
326 | do_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 | |||
366 | static void | ||
367 | do_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 | |||
402 | static void | ||
403 | do_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 | |||
437 | static void | ||
438 | do_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 | |||
465 | int | ||
466 | main (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_purge_cfg_dir ("perf_gnunet_service_fs_p2p.conf", | ||
475 | "GNUNET_TEST_HOME"); | ||
476 | return ok; | ||
477 | } | ||
478 | |||
479 | |||
480 | /* 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 bbd0ff57b..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 | |||
29 | #include "gnunet_fs_service.h" | ||
30 | #include "block_fs.h" | ||
31 | #include "gnunet_signatures.h" | ||
32 | #include "gnunet_block_group_lib.h" | ||
33 | |||
34 | |||
35 | /** | ||
36 | * Number of bits we set per entry in the bloomfilter. | ||
37 | * Do not change! | ||
38 | */ | ||
39 | #define BLOOMFILTER_K 16 | ||
40 | |||
41 | |||
42 | /** | ||
43 | * Create a new block group. | ||
44 | * | ||
45 | * @param ctx block context in which the block group is created | ||
46 | * @param type type of the block for which we are creating the group | ||
47 | * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh | ||
48 | * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh | ||
49 | * @param va variable arguments specific to @a type | ||
50 | * @return block group handle, NULL if block groups are not supported | ||
51 | * by this @a type of block (this is not an error) | ||
52 | */ | ||
53 | static struct GNUNET_BLOCK_Group * | ||
54 | block_plugin_fs_create_group (void *cls, | ||
55 | enum GNUNET_BLOCK_Type type, | ||
56 | const void *raw_data, | ||
57 | size_t raw_data_size, | ||
58 | va_list va) | ||
59 | { | ||
60 | unsigned int size; | ||
61 | const char *guard; | ||
62 | |||
63 | switch (type) | ||
64 | { | ||
65 | case GNUNET_BLOCK_TYPE_FS_DBLOCK: | ||
66 | GNUNET_break (NULL == va_arg (va, const char *)); | ||
67 | return NULL; | ||
68 | case GNUNET_BLOCK_TYPE_FS_IBLOCK: | ||
69 | GNUNET_break (NULL == va_arg (va, const char *)); | ||
70 | return NULL; | ||
71 | case GNUNET_BLOCK_TYPE_FS_UBLOCK: | ||
72 | guard = va_arg (va, const char *); | ||
73 | if (0 == strcmp (guard, | ||
74 | "seen-set-size")) | ||
75 | { | ||
76 | size = GNUNET_BLOCK_GROUP_compute_bloomfilter_size (va_arg (va, unsigned | ||
77 | int), | ||
78 | BLOOMFILTER_K); | ||
79 | } | ||
80 | else if (0 == strcmp (guard, | ||
81 | "filter-size")) | ||
82 | { | ||
83 | size = va_arg (va, unsigned int); | ||
84 | } | ||
85 | else | ||
86 | { | ||
87 | /* va-args invalid! bad bug, complain! */ | ||
88 | GNUNET_break (0); | ||
89 | size = 8; | ||
90 | } | ||
91 | if (0 == size) | ||
92 | size = raw_data_size; /* not for us to determine, use what we got! */ | ||
93 | GNUNET_break (NULL == va_arg (va, const char *)); | ||
94 | return GNUNET_BLOCK_GROUP_bf_create (cls, | ||
95 | size, | ||
96 | BLOOMFILTER_K, | ||
97 | type, | ||
98 | raw_data, | ||
99 | raw_data_size); | ||
100 | |||
101 | default: | ||
102 | GNUNET_break (NULL == va_arg (va, const char *)); | ||
103 | GNUNET_break (0); | ||
104 | return NULL; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | |||
109 | /** | ||
110 | * Function called to obtain the key for a block. | ||
111 | * | ||
112 | * @param cls closure | ||
113 | * @param type block type | ||
114 | * @param block block to get the key for | ||
115 | * @param block_size number of bytes in @a block | ||
116 | * @param key set to the key (query) for the given block | ||
117 | * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported | ||
118 | * (or if extracting a key from a block of this type does not work) | ||
119 | */ | ||
120 | static enum GNUNET_GenericReturnValue | ||
121 | block_plugin_fs_get_key (void *cls, | ||
122 | enum GNUNET_BLOCK_Type type, | ||
123 | const void *block, | ||
124 | size_t block_size, | ||
125 | struct GNUNET_HashCode *key) | ||
126 | { | ||
127 | const struct UBlock *ub; | ||
128 | |||
129 | switch (type) | ||
130 | { | ||
131 | case GNUNET_BLOCK_TYPE_FS_DBLOCK: | ||
132 | case GNUNET_BLOCK_TYPE_FS_IBLOCK: | ||
133 | GNUNET_CRYPTO_hash (block, | ||
134 | block_size, | ||
135 | key); | ||
136 | return GNUNET_OK; | ||
137 | case GNUNET_BLOCK_TYPE_FS_UBLOCK: | ||
138 | if (block_size < sizeof(struct UBlock)) | ||
139 | { | ||
140 | GNUNET_break_op (0); | ||
141 | memset (key, | ||
142 | 0, | ||
143 | sizeof (*key)); | ||
144 | return GNUNET_OK; | ||
145 | } | ||
146 | ub = block; | ||
147 | GNUNET_CRYPTO_hash (&ub->verification_key, | ||
148 | sizeof(ub->verification_key), | ||
149 | key); | ||
150 | return GNUNET_OK; | ||
151 | default: | ||
152 | GNUNET_break (0); | ||
153 | return GNUNET_SYSERR; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | |||
158 | /** | ||
159 | * Function called to validate a query. | ||
160 | * | ||
161 | * @param cls closure | ||
162 | * @param type block type | ||
163 | * @param query original query (hash) | ||
164 | * @param xquery extended 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 | */ | ||
168 | static enum GNUNET_GenericReturnValue | ||
169 | block_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 | */ | ||
202 | static enum GNUNET_GenericReturnValue | ||
203 | block_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 | */ | ||
264 | static enum GNUNET_BLOCK_ReplyEvaluationResult | ||
265 | block_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 | */ | ||
302 | void * | ||
303 | libgnunet_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 | */ | ||
327 | void * | ||
328 | libgnunet_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 | |||
36 | static char * | ||
37 | makeName (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 | |||
51 | static enum GNUNET_FSUI_EventType lastEvent; | ||
52 | |||
53 | static struct GNUNET_MetaData *search_meta; | ||
54 | |||
55 | static struct GNUNET_ECRS_URI *search_uri; | ||
56 | |||
57 | static struct GNUNET_FSUI_Context *ctx; | ||
58 | |||
59 | static void * | ||
60 | eventCallback (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 | |||
108 | int | ||
109 | main (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 */ | ||
234 | FAILURE: | ||
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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-data/ | ||
4 | |||
5 | [fs] | ||
6 | ACTIVEMIGRATION = 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] | ||
4 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-lib/ | ||
5 | |||
6 | [transport] | ||
7 | PLUGINS = tcp | ||
8 | |||
9 | [nat] | ||
10 | DISABLEV6 = YES | ||
11 | ENABLE_UPNP = NO | ||
12 | BEHIND_NAT = NO | ||
13 | ALLOW_NAT = NO | ||
14 | INTERNAL_ADDRESS = 127.0.0.1 | ||
15 | EXTERNAL_ADDRESS = 127.0.0.1 | ||
16 | USE_LOCALADDR = YES | ||
17 | RETURN_LOCAL_ADDRESSES = YES | ||
18 | |||
19 | [datastore] | ||
20 | QUOTA = 100 MB | ||
21 | |||
22 | [transport-tcp] | ||
23 | BINDTO = 127.0.0.1 | ||
24 | PORT = 54368 | ||
25 | |||
26 | [peerinfo] | ||
27 | NO_IO = YES | ||
28 | |||
29 | [ats] | ||
30 | WAN_QUOTA_IN = 65536 | ||
31 | WAN_QUOTA_OUT = 65536 | ||
32 | |||
33 | [fs] | ||
34 | CONTENT_CACHING = YES | ||
35 | CONTENT_PUSHING = YES | ||
36 | DELAY = YES | ||
37 | # PREFIX = valgrind --leak-check=full | ||
38 | |||
39 | [dhtcache] | ||
40 | QUOTA=65536 | ||
41 | DATABASE=heap | ||
42 | |||
43 | [cadet] | ||
44 | REFRESH_PATH_TIME = 30 min | ||
45 | |||
46 | [nse] | ||
47 | WORKBITS = 0 | ||
diff --git a/src/fs/test_fs_directory.c b/src/fs/test_fs_directory.c deleted file mode 100644 index f5121a3e7..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 | |||
36 | struct PCLS | ||
37 | { | ||
38 | struct GNUNET_FS_Uri **uri; | ||
39 | struct GNUNET_FS_MetaData **md; | ||
40 | unsigned int pos; | ||
41 | unsigned int max; | ||
42 | }; | ||
43 | |||
44 | static void | ||
45 | processor (void *cls, const char *filename, const struct GNUNET_FS_Uri *uri, | ||
46 | const struct GNUNET_FS_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_FS_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 | |||
67 | static int | ||
68 | testDirectory (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_FS_MetaData **mds; | ||
75 | struct GNUNET_FS_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_FS_MetaData *) * i); | ||
89 | meta = GNUNET_FS_meta_data_create (); | ||
90 | GNUNET_FS_meta_data_insert (meta, "<test>", EXTRACTOR_METATYPE_TITLE, | ||
91 | EXTRACTOR_METAFORMAT_UTF8, "text/plain", | ||
92 | "A title", strlen ("A title") + 1); | ||
93 | GNUNET_FS_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_FS_meta_data_create (); | ||
100 | for (q = 0; q <= p; q++) | ||
101 | { | ||
102 | GNUNET_snprintf (txt, sizeof(txt), "%u -- %u\n", p, q); | ||
103 | GNUNET_FS_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_FS_meta_data_destroy (mds[p]); | ||
120 | while (--p > 0) | ||
121 | { | ||
122 | GNUNET_FS_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_FS_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_FS_meta_data_destroy (meta); | ||
154 | for (p = 0; p < i; p++) | ||
155 | { | ||
156 | GNUNET_FS_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 | |||
165 | int | ||
166 | main (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 fc6b32c0f..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 | |||
48 | static unsigned int anonymity_level; | ||
49 | |||
50 | static int indexed; | ||
51 | |||
52 | static struct GNUNET_TIME_Absolute start; | ||
53 | |||
54 | static struct GNUNET_FS_Handle *fs; | ||
55 | |||
56 | static struct GNUNET_FS_DownloadContext *download; | ||
57 | |||
58 | static struct GNUNET_FS_PublishContext *publish; | ||
59 | |||
60 | static struct GNUNET_SCHEDULER_Task *timeout_kill; | ||
61 | |||
62 | static char *fn; | ||
63 | |||
64 | static char *fn1; | ||
65 | |||
66 | static int err; | ||
67 | |||
68 | |||
69 | static void | ||
70 | timeout_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 | |||
88 | static void | ||
89 | abort_publish_task (void *cls) | ||
90 | { | ||
91 | if (NULL != publish) | ||
92 | { | ||
93 | GNUNET_FS_publish_stop (publish); | ||
94 | publish = NULL; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | |||
99 | static void | ||
100 | stop_fs_task (void *cls) | ||
101 | { | ||
102 | GNUNET_FS_stop (fs); | ||
103 | fs = NULL; | ||
104 | } | ||
105 | |||
106 | |||
107 | static void | ||
108 | abort_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 | |||
128 | static void * | ||
129 | progress_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 | |||
259 | static void | ||
260 | run (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_FS_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_FS_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_FS_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 | |||
337 | int | ||
338 | main (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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-download/ | ||
4 | |||
5 | [download-test] | ||
6 | # set to 'YES' to test non-anonymous download | ||
7 | USE_STREAM = NO | ||
8 | |||
9 | # set to 'YES' to use indexing | ||
10 | USE_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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-download/ | ||
4 | |||
5 | [download-test] | ||
6 | # set to 'YES' to test non-anonymous download | ||
7 | USE_STREAM = NO | ||
8 | |||
9 | # set to 'YES' to use indexing | ||
10 | USE_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 b66fefd6b..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 | |||
47 | static struct GNUNET_TIME_Absolute start; | ||
48 | |||
49 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
50 | |||
51 | static struct GNUNET_FS_Handle *fs; | ||
52 | |||
53 | static struct GNUNET_FS_DownloadContext *download; | ||
54 | |||
55 | static struct GNUNET_FS_PublishContext *publish; | ||
56 | |||
57 | static struct GNUNET_SCHEDULER_Task *timeout_kill; | ||
58 | |||
59 | static char *fn; | ||
60 | |||
61 | static int err; | ||
62 | |||
63 | |||
64 | static void | ||
65 | timeout_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 | |||
83 | static void | ||
84 | abort_publish_task (void *cls) | ||
85 | { | ||
86 | if (publish != NULL) | ||
87 | { | ||
88 | GNUNET_FS_publish_stop (publish); | ||
89 | publish = NULL; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | |||
94 | static void | ||
95 | abort_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 | |||
115 | static void * | ||
116 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event); | ||
117 | |||
118 | |||
119 | static void | ||
120 | restart_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 | */ | ||
136 | static void | ||
137 | consider_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 | |||
152 | static void * | ||
153 | progress_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 | |||
295 | static void | ||
296 | run (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_FS_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_FS_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_FS_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 | |||
340 | int | ||
341 | main (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 15380bfc4..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 | |||
50 | static int | ||
51 | mycleaner (void *cls, struct GNUNET_FS_FileInformation *fi, uint64_t length, | ||
52 | struct GNUNET_FS_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 | |||
59 | static void | ||
60 | run (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_FS_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_FS_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_FS_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 | |||
140 | int | ||
141 | main (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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-file-information/ | ||
4 | |||
5 | [transport] | ||
6 | PLUGINS = | ||
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 | |||
29 | int | ||
30 | main (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 7e06c47f5..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 | |||
51 | static struct GNUNET_TIME_Absolute start; | ||
52 | |||
53 | static struct GNUNET_FS_Handle *fs; | ||
54 | |||
55 | static struct GNUNET_FS_PublishContext *publish; | ||
56 | |||
57 | static char *fn1; | ||
58 | |||
59 | static char *fn2; | ||
60 | |||
61 | static int err; | ||
62 | |||
63 | |||
64 | static void | ||
65 | abort_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 | |||
78 | static void | ||
79 | list_indexed_task (void *cls) | ||
80 | { | ||
81 | GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL); | ||
82 | } | ||
83 | |||
84 | |||
85 | static void * | ||
86 | progress_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 | |||
176 | static void | ||
177 | run (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_FS_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_FS_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_FS_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 | |||
254 | int | ||
255 | main (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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-list-indexed/ | ||
4 | |||
5 | [transport] | ||
6 | PLUGINS = | ||
7 | |||
8 | [fs] | ||
9 | ACTIVEMIGRATION = NO | ||
10 | |||
diff --git a/src/fs/test_fs_meta_data.c b/src/fs/test_fs_meta_data.c deleted file mode 100644 index 4e7439d7b..000000000 --- a/src/fs/test_fs_meta_data.c +++ /dev/null | |||
@@ -1,492 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2003, 2004, 2006, 2009, 2010, 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 | /** | ||
22 | * @file util/test_fs_meta_data.c | ||
23 | * @brief Test for fs_meta_data.c | ||
24 | * @author Christian Grothoff | ||
25 | * @author Martin Schanzenbach | ||
26 | */ | ||
27 | |||
28 | |||
29 | #include "platform.h" | ||
30 | #include "gnunet_util_lib.h" | ||
31 | |||
32 | #include "gnunet_fs_service.h" | ||
33 | |||
34 | #define ABORT(m) { fprintf (stderr, "Error at %s:%d\n", __FILE__, __LINE__); \ | ||
35 | if (m != NULL) GNUNET_FS_meta_data_destroy (m); \ | ||
36 | return 1; } | ||
37 | |||
38 | |||
39 | static int | ||
40 | testMeta (int i) | ||
41 | { | ||
42 | struct GNUNET_FS_MetaData *m; | ||
43 | char val[256]; | ||
44 | char *sval; | ||
45 | int j; | ||
46 | unsigned int size; | ||
47 | |||
48 | m = GNUNET_FS_meta_data_create (); | ||
49 | if (GNUNET_OK != | ||
50 | GNUNET_FS_meta_data_insert (m, "<test>", EXTRACTOR_METATYPE_TITLE, | ||
51 | EXTRACTOR_METAFORMAT_UTF8, | ||
52 | "text/plain", "TestTitle", | ||
53 | strlen ("TestTitle") + 1)) | ||
54 | ABORT (m); | ||
55 | if (GNUNET_OK != | ||
56 | GNUNET_FS_meta_data_insert (m, "<test>", | ||
57 | EXTRACTOR_METATYPE_AUTHOR_NAME, | ||
58 | EXTRACTOR_METAFORMAT_UTF8, | ||
59 | "text/plain", "TestTitle", | ||
60 | strlen ("TestTitle") + 1)) | ||
61 | ABORT (m); | ||
62 | if (GNUNET_OK == GNUNET_FS_meta_data_insert (m, "<test>", | ||
63 | EXTRACTOR_METATYPE_TITLE, | ||
64 | EXTRACTOR_METAFORMAT_UTF8, | ||
65 | "text/plain", | ||
66 | "TestTitle", strlen ( | ||
67 | "TestTitle") + 1)) /* dup! */ | ||
68 | ABORT (m); | ||
69 | if (GNUNET_OK == GNUNET_FS_meta_data_insert (m, "<test>", | ||
70 | EXTRACTOR_METATYPE_AUTHOR_NAME, | ||
71 | EXTRACTOR_METAFORMAT_UTF8, | ||
72 | "text/plain", | ||
73 | "TestTitle", strlen ( | ||
74 | "TestTitle") + 1)) /* dup! */ | ||
75 | ABORT (m); | ||
76 | if (2 != GNUNET_FS_meta_data_iterate (m, NULL, NULL)) | ||
77 | ABORT (m); | ||
78 | if (GNUNET_OK != | ||
79 | GNUNET_FS_meta_data_delete (m, EXTRACTOR_METATYPE_AUTHOR_NAME, | ||
80 | "TestTitle", strlen ("TestTitle") + 1)) | ||
81 | ABORT (m); | ||
82 | if (GNUNET_OK == GNUNET_FS_meta_data_delete (m, | ||
83 | EXTRACTOR_METATYPE_AUTHOR_NAME, | ||
84 | "TestTitle", strlen ( | ||
85 | "TestTitle") + 1)) /* already gone */ | ||
86 | ABORT (m); | ||
87 | if (1 != GNUNET_FS_meta_data_iterate (m, NULL, NULL)) | ||
88 | ABORT (m); | ||
89 | if (GNUNET_OK != | ||
90 | GNUNET_FS_meta_data_delete (m, EXTRACTOR_METATYPE_TITLE, | ||
91 | "TestTitle", strlen ("TestTitle") + 1)) | ||
92 | ABORT (m); | ||
93 | if (GNUNET_OK == GNUNET_FS_meta_data_delete (m, | ||
94 | EXTRACTOR_METATYPE_TITLE, | ||
95 | "TestTitle", strlen ( | ||
96 | "TestTitle") + 1)) /* already gone */ | ||
97 | ABORT (m); | ||
98 | if (0 != GNUNET_FS_meta_data_iterate (m, NULL, NULL)) | ||
99 | ABORT (m); | ||
100 | for (j = 0; j < i; j++) | ||
101 | { | ||
102 | GNUNET_snprintf (val, sizeof(val), "%s.%d", | ||
103 | "A teststring that should compress well.", j); | ||
104 | if (GNUNET_OK != | ||
105 | GNUNET_FS_meta_data_insert (m, "<test>", | ||
106 | EXTRACTOR_METATYPE_UNKNOWN, | ||
107 | EXTRACTOR_METAFORMAT_UTF8, | ||
108 | "text/plain", val, strlen (val) + 1)) | ||
109 | ABORT (m); | ||
110 | } | ||
111 | if (i != GNUNET_FS_meta_data_iterate (m, NULL, NULL)) | ||
112 | ABORT (m); | ||
113 | |||
114 | size = GNUNET_FS_meta_data_get_serialized_size (m); | ||
115 | sval = NULL; | ||
116 | if (size != | ||
117 | GNUNET_FS_meta_data_serialize (m, &sval, size, | ||
118 | GNUNET_FS_META_DATA_SERIALIZE_FULL)) | ||
119 | { | ||
120 | GNUNET_free (sval); | ||
121 | ABORT (m); | ||
122 | } | ||
123 | GNUNET_FS_meta_data_destroy (m); | ||
124 | m = GNUNET_FS_meta_data_deserialize (sval, size); | ||
125 | GNUNET_free (sval); | ||
126 | if (m == NULL) | ||
127 | ABORT (m); | ||
128 | for (j = 0; j < i; j++) | ||
129 | { | ||
130 | GNUNET_snprintf (val, | ||
131 | sizeof(val), | ||
132 | "%s.%d", | ||
133 | "A teststring that should compress well.", | ||
134 | j); | ||
135 | if (GNUNET_OK != | ||
136 | GNUNET_FS_meta_data_delete (m, | ||
137 | EXTRACTOR_METATYPE_UNKNOWN, | ||
138 | val, | ||
139 | strlen (val) + 1)) | ||
140 | { | ||
141 | ABORT (m); | ||
142 | } | ||
143 | } | ||
144 | if (0 != GNUNET_FS_meta_data_iterate (m, NULL, NULL)) | ||
145 | ABORT (m); | ||
146 | GNUNET_FS_meta_data_destroy (m); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | |||
151 | static int | ||
152 | testMetaMore (int i) | ||
153 | { | ||
154 | struct GNUNET_FS_MetaData *meta; | ||
155 | int q; | ||
156 | char txt[128]; | ||
157 | char *data; | ||
158 | unsigned long long size; | ||
159 | |||
160 | meta = GNUNET_FS_meta_data_create (); | ||
161 | for (q = 0; q <= i; q++) | ||
162 | { | ||
163 | GNUNET_snprintf (txt, 128, "%u -- %u\n", i, q); | ||
164 | GNUNET_FS_meta_data_insert (meta, "<test>", | ||
165 | q | ||
166 | % 42 /* EXTRACTOR_metatype_get_max () */, | ||
167 | EXTRACTOR_METAFORMAT_UTF8, "text/plain", | ||
168 | txt, strlen (txt) + 1); | ||
169 | } | ||
170 | size = GNUNET_FS_meta_data_get_serialized_size (meta); | ||
171 | data = GNUNET_malloc (size * 4); | ||
172 | if (size != | ||
173 | GNUNET_FS_meta_data_serialize (meta, &data, size * 4, | ||
174 | GNUNET_FS_META_DATA_SERIALIZE_FULL)) | ||
175 | { | ||
176 | GNUNET_free (data); | ||
177 | ABORT (meta); | ||
178 | } | ||
179 | GNUNET_FS_meta_data_destroy (meta); | ||
180 | GNUNET_free (data); | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | |||
185 | static int | ||
186 | testMetaLink () | ||
187 | { | ||
188 | struct GNUNET_FS_MetaData *m; | ||
189 | char *val; | ||
190 | unsigned int size; | ||
191 | |||
192 | m = GNUNET_FS_meta_data_create (); | ||
193 | if (GNUNET_OK != | ||
194 | GNUNET_FS_meta_data_insert (m, "<test>", | ||
195 | EXTRACTOR_METATYPE_UNKNOWN, | ||
196 | EXTRACTOR_METAFORMAT_UTF8, | ||
197 | "text/plain", "link", | ||
198 | strlen ("link") + 1)) | ||
199 | ABORT (m); | ||
200 | if (GNUNET_OK != | ||
201 | GNUNET_FS_meta_data_insert (m, "<test>", | ||
202 | EXTRACTOR_METATYPE_FILENAME, | ||
203 | EXTRACTOR_METAFORMAT_UTF8, | ||
204 | "text/plain", "lib-link.m4", | ||
205 | strlen ("lib-link.m4") + 1)) | ||
206 | ABORT (m); | ||
207 | val = NULL; | ||
208 | size = | ||
209 | GNUNET_FS_meta_data_serialize (m, &val, (size_t) -1, | ||
210 | GNUNET_FS_META_DATA_SERIALIZE_FULL); | ||
211 | GNUNET_FS_meta_data_destroy (m); | ||
212 | m = GNUNET_FS_meta_data_deserialize (val, size); | ||
213 | GNUNET_free (val); | ||
214 | if (m == NULL) | ||
215 | ABORT (m); | ||
216 | GNUNET_FS_meta_data_destroy (m); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | |||
221 | static int | ||
222 | check () | ||
223 | { | ||
224 | struct GNUNET_FS_MetaData *meta; | ||
225 | struct GNUNET_FS_MetaData *meta2; | ||
226 | int q; | ||
227 | int i = 100; | ||
228 | char txt[128]; | ||
229 | char *str; | ||
230 | unsigned char *thumb; | ||
231 | |||
232 | meta = GNUNET_FS_meta_data_create (); | ||
233 | meta2 = GNUNET_FS_meta_data_create (); | ||
234 | for (q = 0; q <= i; q++) | ||
235 | { | ||
236 | GNUNET_snprintf (txt, 128, "%u -- %u\n", i, q); | ||
237 | GNUNET_FS_meta_data_insert (meta, "<test>", | ||
238 | EXTRACTOR_METATYPE_UNKNOWN, | ||
239 | EXTRACTOR_METAFORMAT_UTF8, "text/plain", | ||
240 | "TestTitle", strlen ("TestTitle") + 1); | ||
241 | GNUNET_FS_meta_data_insert (meta2, "<test>", | ||
242 | EXTRACTOR_METATYPE_UNKNOWN, | ||
243 | EXTRACTOR_METAFORMAT_UTF8, "text/plain", | ||
244 | "TestTitle", strlen ("TestTitle") + 1); | ||
245 | } | ||
246 | |||
247 | // check meta_data_test_equal | ||
248 | if (GNUNET_YES != GNUNET_FS_meta_data_test_equal (meta, meta2)) | ||
249 | { | ||
250 | GNUNET_FS_meta_data_destroy (meta2); | ||
251 | ABORT (meta); | ||
252 | } | ||
253 | |||
254 | // check meta_data_clear | ||
255 | GNUNET_FS_meta_data_clear (meta2); | ||
256 | if (0 != GNUNET_FS_meta_data_iterate (meta2, NULL, NULL)) | ||
257 | { | ||
258 | GNUNET_FS_meta_data_destroy (meta2); | ||
259 | ABORT (meta); | ||
260 | } | ||
261 | // check equal branch in meta_data_test_equal | ||
262 | if (GNUNET_YES != GNUNET_FS_meta_data_test_equal (meta, meta)) | ||
263 | { | ||
264 | GNUNET_FS_meta_data_destroy (meta2); | ||
265 | ABORT (meta); | ||
266 | } | ||
267 | // check "count" branch in meta_data_test_equal | ||
268 | if (GNUNET_NO != GNUNET_FS_meta_data_test_equal (meta, meta2)) | ||
269 | { | ||
270 | GNUNET_FS_meta_data_destroy (meta2); | ||
271 | ABORT (meta); | ||
272 | } | ||
273 | |||
274 | // check meta_data_add_publication_date | ||
275 | GNUNET_FS_meta_data_add_publication_date (meta2); | ||
276 | |||
277 | // check meta_data_merge | ||
278 | GNUNET_FS_meta_data_clear (meta2); | ||
279 | GNUNET_FS_meta_data_merge (meta2, meta); | ||
280 | if (100 == GNUNET_FS_meta_data_iterate (meta2, NULL, NULL)) | ||
281 | { | ||
282 | GNUNET_FS_meta_data_destroy (meta2); | ||
283 | ABORT (meta); | ||
284 | } | ||
285 | |||
286 | // check meta_data_get_by_type | ||
287 | GNUNET_FS_meta_data_clear (meta2); | ||
288 | if (NULL != | ||
289 | (str = | ||
290 | GNUNET_FS_meta_data_get_by_type (meta2, | ||
291 | EXTRACTOR_METATYPE_UNKNOWN))) | ||
292 | { | ||
293 | GNUNET_FS_meta_data_destroy (meta2); | ||
294 | GNUNET_free (str); | ||
295 | ABORT (meta); | ||
296 | } | ||
297 | |||
298 | str = | ||
299 | GNUNET_FS_meta_data_get_by_type (meta, EXTRACTOR_METATYPE_UNKNOWN); | ||
300 | GNUNET_assert (NULL != str); | ||
301 | if (str[0] != 'T') | ||
302 | { | ||
303 | GNUNET_FS_meta_data_destroy (meta2); | ||
304 | GNUNET_free (str); | ||
305 | ABORT (meta); | ||
306 | } | ||
307 | GNUNET_free (str); | ||
308 | |||
309 | // check branch | ||
310 | if (NULL != | ||
311 | (str = | ||
312 | GNUNET_FS_meta_data_get_by_type (meta, | ||
313 | EXTRACTOR_METATYPE_PUBLICATION_DATE))) | ||
314 | { | ||
315 | GNUNET_free (str); | ||
316 | GNUNET_FS_meta_data_destroy (meta2); | ||
317 | ABORT (meta); | ||
318 | } | ||
319 | |||
320 | // check meta_data_get_first_by_types | ||
321 | str = | ||
322 | GNUNET_FS_meta_data_get_first_by_types (meta, | ||
323 | EXTRACTOR_METATYPE_UNKNOWN, | ||
324 | -1); | ||
325 | GNUNET_assert (NULL != str); | ||
326 | if (str[0] != 'T') | ||
327 | { | ||
328 | GNUNET_FS_meta_data_destroy (meta2); | ||
329 | GNUNET_free (str); | ||
330 | ABORT (meta); | ||
331 | } | ||
332 | GNUNET_free (str); | ||
333 | |||
334 | // check meta_data_get_thumbnail | ||
335 | if (GNUNET_FS_meta_data_get_thumbnail (meta, &thumb) != 0) | ||
336 | { | ||
337 | GNUNET_free (thumb); | ||
338 | GNUNET_FS_meta_data_destroy (meta2); | ||
339 | ABORT (meta); | ||
340 | } | ||
341 | GNUNET_FS_meta_data_destroy (meta2); | ||
342 | // check meta_data_duplicate | ||
343 | meta2 = GNUNET_FS_meta_data_duplicate (meta); | ||
344 | if (200 == GNUNET_FS_meta_data_iterate (meta2, NULL, NULL)) | ||
345 | { | ||
346 | GNUNET_FS_meta_data_destroy (meta2); | ||
347 | ABORT (meta); | ||
348 | } | ||
349 | GNUNET_FS_meta_data_destroy (meta2); | ||
350 | GNUNET_FS_meta_data_destroy (meta); | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | |||
355 | static int | ||
356 | test_bigmeta_rw (void) | ||
357 | { | ||
358 | static char meta[1024 * 1024 * 10]; | ||
359 | struct GNUNET_BIO_WriteHandle *wh; | ||
360 | struct GNUNET_BIO_ReadHandle *rh; | ||
361 | char *filename = GNUNET_DISK_mktemp ("gnunet_bio"); | ||
362 | struct GNUNET_FS_MetaData *mdR = NULL; | ||
363 | |||
364 | memset (meta, 'b', sizeof (meta)); | ||
365 | meta[sizeof (meta) - 1] = '\0'; | ||
366 | |||
367 | wh = GNUNET_BIO_write_open_file (filename); | ||
368 | GNUNET_assert (NULL != wh); | ||
369 | if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, | ||
370 | "test-bigmeta-rw-int32", | ||
371 | sizeof (meta))) | ||
372 | { | ||
373 | GNUNET_BIO_write_close (wh, NULL); | ||
374 | return 1; | ||
375 | } | ||
376 | if (GNUNET_OK != GNUNET_BIO_write (wh, | ||
377 | "test-bigmeta-rw-bytes", | ||
378 | meta, | ||
379 | sizeof (meta))) | ||
380 | { | ||
381 | GNUNET_BIO_write_close (wh, NULL); | ||
382 | return 1; | ||
383 | } | ||
384 | GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (wh, NULL)); | ||
385 | |||
386 | rh = GNUNET_BIO_read_open_file (filename); | ||
387 | GNUNET_assert (NULL != rh); | ||
388 | GNUNET_assert (GNUNET_SYSERR == | ||
389 | GNUNET_FS_read_meta_data (rh, | ||
390 | "test-bigmeta-rw-metadata", | ||
391 | &mdR)); | ||
392 | GNUNET_assert (GNUNET_SYSERR == GNUNET_BIO_read_close (rh, NULL)); | ||
393 | |||
394 | GNUNET_assert (NULL == mdR); | ||
395 | |||
396 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (filename)); | ||
397 | GNUNET_free (filename); | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int | ||
402 | test_fakemeta_rw (void) | ||
403 | { | ||
404 | struct GNUNET_BIO_WriteHandle *wh; | ||
405 | struct GNUNET_BIO_ReadHandle *rh; | ||
406 | char *filename = GNUNET_DISK_mktemp ("gnunet_bio"); | ||
407 | struct GNUNET_FS_MetaData *mdR = NULL; | ||
408 | |||
409 | wh = GNUNET_BIO_write_open_file (filename); | ||
410 | GNUNET_assert (NULL != wh); | ||
411 | if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, | ||
412 | "test-fakestring-rw-int32", | ||
413 | 2)) | ||
414 | { | ||
415 | GNUNET_BIO_write_close (wh, NULL); | ||
416 | return 1; | ||
417 | } | ||
418 | GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (wh, NULL)); | ||
419 | |||
420 | rh = GNUNET_BIO_read_open_file (filename); | ||
421 | GNUNET_assert (NULL != rh); | ||
422 | GNUNET_assert (GNUNET_SYSERR == | ||
423 | GNUNET_FS_read_meta_data (rh, | ||
424 | "test-fakestring-rw-metadata", | ||
425 | &mdR)); | ||
426 | GNUNET_assert (GNUNET_SYSERR == GNUNET_BIO_read_close (rh, NULL)); | ||
427 | |||
428 | GNUNET_assert (NULL == mdR); | ||
429 | |||
430 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (filename)); | ||
431 | GNUNET_free (filename); | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static int | ||
436 | test_fakebigmeta_rw (void) | ||
437 | { | ||
438 | struct GNUNET_BIO_WriteHandle *wh; | ||
439 | struct GNUNET_BIO_ReadHandle *rh; | ||
440 | char *filename = GNUNET_DISK_mktemp ("gnunet_bio"); | ||
441 | struct GNUNET_FS_MetaData *mdR = NULL; | ||
442 | int32_t wNum = 1024 * 1024 * 10; | ||
443 | |||
444 | wh = GNUNET_BIO_write_open_file (filename); | ||
445 | GNUNET_assert (NULL != wh); | ||
446 | GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_int32 (wh, | ||
447 | "test-fakebigmeta-rw-int32", | ||
448 | wNum)); | ||
449 | GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (wh, NULL)); | ||
450 | |||
451 | rh = GNUNET_BIO_read_open_file (filename); | ||
452 | GNUNET_assert (NULL != rh); | ||
453 | GNUNET_assert (GNUNET_SYSERR == | ||
454 | GNUNET_FS_read_meta_data (rh, | ||
455 | "test-fakebigmeta-rw-metadata", | ||
456 | &mdR)); | ||
457 | GNUNET_assert (GNUNET_SYSERR == GNUNET_BIO_read_close (rh, NULL)); | ||
458 | |||
459 | GNUNET_assert (NULL == mdR); | ||
460 | |||
461 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (filename)); | ||
462 | GNUNET_free (filename); | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | int | ||
467 | main (int argc, char *argv[]) | ||
468 | { | ||
469 | int failureCount = 0; | ||
470 | int i; | ||
471 | |||
472 | GNUNET_log_setup ("test-fs-meta-data", "WARNING", NULL); | ||
473 | for (i = 0; i < 255; i++) | ||
474 | failureCount += testMeta (i); | ||
475 | for (i = 1; i < 255; i++) | ||
476 | failureCount += testMetaMore (i); | ||
477 | failureCount += testMetaLink (); | ||
478 | failureCount += test_fakebigmeta_rw (); | ||
479 | failureCount += test_fakemeta_rw (); | ||
480 | failureCount += test_bigmeta_rw (); | ||
481 | int ret = check (); | ||
482 | |||
483 | if (ret == 1) | ||
484 | return 1; | ||
485 | |||
486 | if (failureCount != 0) | ||
487 | return 1; | ||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | |||
492 | /* end of test_container_meta_data.c */ | ||
diff --git a/src/fs/test_fs_namespace.c b/src/fs/test_fs_namespace.c deleted file mode 100644 index 85d489598..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 | |||
32 | static struct GNUNET_CRYPTO_EcdsaPublicKey nsid; | ||
33 | |||
34 | static struct GNUNET_FS_Uri *sks_expect_uri; | ||
35 | |||
36 | static struct GNUNET_FS_Uri *ksk_expect_uri; | ||
37 | |||
38 | static struct GNUNET_FS_Handle *fs; | ||
39 | |||
40 | static struct GNUNET_FS_SearchContext *sks_search; | ||
41 | |||
42 | static struct GNUNET_FS_SearchContext *ksk_search; | ||
43 | |||
44 | static struct GNUNET_SCHEDULER_Task *kill_task; | ||
45 | |||
46 | static int update_started; | ||
47 | |||
48 | static int err; | ||
49 | |||
50 | |||
51 | static void | ||
52 | abort_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 | |||
68 | static void | ||
69 | abort_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 | |||
84 | static void | ||
85 | do_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 | |||
95 | static void * | ||
96 | progress_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 | |||
167 | static void | ||
168 | publish_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 | |||
206 | static void | ||
207 | sks_cont (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg) | ||
208 | { | ||
209 | struct GNUNET_FS_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_FS_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_FS_meta_data_destroy (meta); | ||
235 | } | ||
236 | |||
237 | |||
238 | static void | ||
239 | adv_cont (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg) | ||
240 | { | ||
241 | struct GNUNET_FS_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_FS_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_FS_meta_data_destroy (meta); | ||
266 | } | ||
267 | |||
268 | |||
269 | static void | ||
270 | testNamespace (void) | ||
271 | { | ||
272 | struct GNUNET_FS_BlockOptions bo; | ||
273 | struct GNUNET_FS_MetaData *meta; | ||
274 | struct GNUNET_FS_Uri *ksk_uri; | ||
275 | struct GNUNET_FS_Uri *sks_uri; | ||
276 | |||
277 | meta = GNUNET_FS_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_FS_meta_data_destroy (meta); | ||
295 | } | ||
296 | |||
297 | |||
298 | static void | ||
299 | run (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 | |||
309 | int | ||
310 | main (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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-namespace/ | ||
4 | |||
5 | [transport] | ||
6 | PLUGINS = | ||
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 d883b7bea..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 | |||
32 | static struct GNUNET_FS_Handle *fs; | ||
33 | |||
34 | static int err; | ||
35 | |||
36 | static struct GNUNET_CRYPTO_EcdsaPrivateKey ns; | ||
37 | |||
38 | static struct GNUNET_FS_MetaData *meta; | ||
39 | |||
40 | static struct GNUNET_FS_Uri *uri_this; | ||
41 | |||
42 | static struct GNUNET_FS_Uri *uri_next; | ||
43 | |||
44 | static struct GNUNET_FS_BlockOptions bo; | ||
45 | |||
46 | |||
47 | static void * | ||
48 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event) | ||
49 | { | ||
50 | return NULL; | ||
51 | } | ||
52 | |||
53 | |||
54 | static void | ||
55 | do_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_FS_meta_data_destroy (meta); | ||
63 | } | ||
64 | |||
65 | |||
66 | static void | ||
67 | check_next (void *cls, const char *last_id, | ||
68 | const struct GNUNET_FS_Uri *last_uri, | ||
69 | const struct GNUNET_FS_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 | |||
78 | static void | ||
79 | check_this_next (void *cls, const char *last_id, | ||
80 | const struct GNUNET_FS_Uri *last_uri, | ||
81 | const struct GNUNET_FS_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 | |||
92 | static void | ||
93 | sks_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 | |||
101 | static void | ||
102 | check_this (void *cls, const char *last_id, | ||
103 | const struct GNUNET_FS_Uri *last_uri, | ||
104 | const struct GNUNET_FS_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 | |||
113 | static void | ||
114 | sks_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 | |||
125 | static void | ||
126 | testNamespace (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_FS_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 | |||
152 | static void | ||
153 | run (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 | |||
163 | int | ||
164 | main (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 0e379bc29..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 | |||
47 | static struct GNUNET_TIME_Absolute start; | ||
48 | |||
49 | static struct GNUNET_FS_Handle *fs; | ||
50 | |||
51 | static struct GNUNET_FS_PublishContext *publish; | ||
52 | |||
53 | static char *fn1; | ||
54 | |||
55 | static char *fn2; | ||
56 | |||
57 | static int err; | ||
58 | |||
59 | |||
60 | static void | ||
61 | abort_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 | |||
74 | static void * | ||
75 | progress_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 | |||
162 | static void | ||
163 | run (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_FS_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_FS_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_FS_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 | |||
240 | int | ||
241 | main (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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-publish/ | ||
4 | |||
5 | [transport] | ||
6 | PLUGINS = | ||
7 | |||
8 | [fs] | ||
9 | ACTIVEMIGRATION = 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 e1563f448..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 | |||
47 | static struct GNUNET_TIME_Absolute start; | ||
48 | |||
49 | static struct GNUNET_FS_Handle *fs; | ||
50 | |||
51 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
52 | |||
53 | static struct GNUNET_FS_PublishContext *publish; | ||
54 | |||
55 | static struct GNUNET_FS_PublishContext *publish; | ||
56 | |||
57 | static char *fn1; | ||
58 | |||
59 | static char *fn2; | ||
60 | |||
61 | static int err; | ||
62 | |||
63 | static struct GNUNET_SCHEDULER_Task *rtask; | ||
64 | |||
65 | |||
66 | static void | ||
67 | abort_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 | |||
87 | static void * | ||
88 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event); | ||
89 | |||
90 | |||
91 | static void | ||
92 | restart_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 | */ | ||
110 | static void | ||
111 | consider_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 | |||
127 | static void * | ||
128 | progress_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 | |||
235 | static void | ||
236 | run (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_FS_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_FS_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_FS_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 | |||
311 | int | ||
312 | main (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 f9266582e..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 | |||
47 | static struct GNUNET_TIME_Absolute start; | ||
48 | |||
49 | static struct GNUNET_FS_Handle *fs; | ||
50 | |||
51 | static struct GNUNET_FS_SearchContext *search; | ||
52 | |||
53 | static struct GNUNET_FS_PublishContext *publish; | ||
54 | |||
55 | static struct GNUNET_SCHEDULER_Task *timeout_task; | ||
56 | |||
57 | static int err; | ||
58 | |||
59 | |||
60 | static void | ||
61 | abort_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 | |||
76 | static void | ||
77 | abort_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 | |||
96 | static void | ||
97 | abort_search_task (void *cls) | ||
98 | { | ||
99 | if (NULL != search) | ||
100 | { | ||
101 | GNUNET_FS_search_stop (search); | ||
102 | search = NULL; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | |||
107 | static void * | ||
108 | progress_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 | |||
197 | static void | ||
198 | run (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_FS_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_FS_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_FS_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 | |||
241 | int | ||
242 | main (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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-search/ | ||
4 | |||
5 | [transport] | ||
6 | PLUGINS = | ||
7 | |||
diff --git a/src/fs/test_fs_search_persistence.c b/src/fs/test_fs_search_persistence.c deleted file mode 100644 index 4ddd40e73..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 | |||
47 | static struct GNUNET_TIME_Absolute start; | ||
48 | |||
49 | static struct GNUNET_FS_Handle *fs; | ||
50 | |||
51 | static struct GNUNET_FS_SearchContext *search; | ||
52 | |||
53 | static struct GNUNET_FS_PublishContext *publish; | ||
54 | |||
55 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
56 | |||
57 | static struct GNUNET_SCHEDULER_Task *timeout_task; | ||
58 | |||
59 | static int err; | ||
60 | |||
61 | |||
62 | static void | ||
63 | abort_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 | |||
82 | static void | ||
83 | abort_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 | |||
98 | static void | ||
99 | abort_search_task (void *cls) | ||
100 | { | ||
101 | if (NULL != search) | ||
102 | { | ||
103 | GNUNET_FS_search_stop (search); | ||
104 | search = NULL; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | |||
109 | static void * | ||
110 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event); | ||
111 | |||
112 | |||
113 | static void | ||
114 | restart_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 | */ | ||
129 | static void | ||
130 | consider_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 | |||
145 | static void * | ||
146 | progress_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 | |||
262 | static void | ||
263 | run (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_FS_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_FS_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_FS_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 | |||
307 | int | ||
308 | main (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 776babaee..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 | |||
48 | static struct GNUNET_TIME_Absolute start; | ||
49 | |||
50 | static struct GNUNET_FS_Handle *fs; | ||
51 | |||
52 | static struct GNUNET_FS_SearchContext *search; | ||
53 | |||
54 | static struct GNUNET_FS_PublishContext *publish; | ||
55 | |||
56 | static struct GNUNET_SCHEDULER_Task *timeout_task; | ||
57 | |||
58 | static int err; | ||
59 | |||
60 | |||
61 | static void | ||
62 | abort_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 | |||
81 | static void | ||
82 | abort_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 | |||
97 | static void | ||
98 | abort_search_task (void *cls) | ||
99 | { | ||
100 | if (search != NULL) | ||
101 | GNUNET_FS_search_stop (search); | ||
102 | search = NULL; | ||
103 | } | ||
104 | |||
105 | |||
106 | static void * | ||
107 | progress_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 | |||
202 | static void | ||
203 | run (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_FS_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_FS_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_FS_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 | |||
247 | int | ||
248 | main (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 9c20936b6..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 | |||
53 | static struct GNUNET_TIME_Absolute start; | ||
54 | |||
55 | static struct GNUNET_FS_Handle *fs; | ||
56 | |||
57 | static struct GNUNET_FS_SearchContext *search; | ||
58 | |||
59 | static struct GNUNET_FS_PublishContext *publish; | ||
60 | |||
61 | static struct GNUNET_SCHEDULER_Task *timeout_task; | ||
62 | |||
63 | static int err; | ||
64 | |||
65 | static int processed_files; | ||
66 | |||
67 | |||
68 | static void | ||
69 | abort_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 | |||
84 | static void | ||
85 | abort_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 | |||
104 | static void | ||
105 | abort_search_task (void *cls) | ||
106 | { | ||
107 | if (NULL != search) | ||
108 | { | ||
109 | GNUNET_FS_search_stop (search); | ||
110 | search = NULL; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | |||
115 | static void * | ||
116 | progress_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 | |||
209 | static void | ||
210 | run (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_FS_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_FS_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_FS_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 | |||
261 | int | ||
262 | main (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 | |||
32 | static void * | ||
33 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event) | ||
34 | { | ||
35 | return NULL; | ||
36 | } | ||
37 | |||
38 | |||
39 | static void | ||
40 | run (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 | |||
53 | int | ||
54 | main (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 714dd452e..000000000 --- a/src/fs/test_fs_test_lib.c +++ /dev/null | |||
@@ -1,181 +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 | |||
45 | static struct GNUNET_TESTBED_Peer *the_peers[NUM_DAEMONS]; | ||
46 | |||
47 | static struct GNUNET_TIME_Absolute start_time; | ||
48 | |||
49 | static int ret; | ||
50 | |||
51 | |||
52 | static void | ||
53 | do_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 | |||
79 | static void | ||
80 | do_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 | |||
104 | static void | ||
105 | do_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 | */ | ||
137 | static void | ||
138 | run (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 | */ | ||
165 | int | ||
166 | main (int argc, char *argv[]) | ||
167 | { | ||
168 | GNUNET_DISK_purge_cfg_dir ("fs_test_lib_data.conf", | ||
169 | "GNUNET_TEST_HOME"); | ||
170 | (void) GNUNET_TESTBED_test_run ("test_fs_test_lib", | ||
171 | "fs_test_lib_data.conf", | ||
172 | NUM_DAEMONS, | ||
173 | 0, NULL, NULL, | ||
174 | &run, NULL); | ||
175 | GNUNET_DISK_purge_cfg_dir ("fs_test_lib_data.conf", | ||
176 | "GNUNET_TEST_HOME"); | ||
177 | return ret; | ||
178 | } | ||
179 | |||
180 | |||
181 | /* 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 dbc33090d..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 | |||
48 | static struct GNUNET_TIME_Absolute start; | ||
49 | |||
50 | static struct GNUNET_FS_Handle *fs; | ||
51 | |||
52 | static struct GNUNET_FS_UnindexContext *unindex; | ||
53 | |||
54 | static struct GNUNET_FS_PublishContext *publish; | ||
55 | |||
56 | static char *fn; | ||
57 | |||
58 | |||
59 | static void | ||
60 | abort_publish_task (void *cls) | ||
61 | { | ||
62 | GNUNET_FS_publish_stop (publish); | ||
63 | publish = NULL; | ||
64 | } | ||
65 | |||
66 | |||
67 | static void | ||
68 | abort_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 | |||
78 | static void * | ||
79 | progress_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 | |||
177 | static void | ||
178 | run (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_FS_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_FS_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_FS_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 | |||
226 | int | ||
227 | main (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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-unindex/ | ||
4 | |||
5 | [transport] | ||
6 | PLUGINS = | ||
7 | |||
diff --git a/src/fs/test_fs_unindex_persistence.c b/src/fs/test_fs_unindex_persistence.c deleted file mode 100644 index b81ce64ab..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 | |||
47 | static struct GNUNET_TIME_Absolute start; | ||
48 | |||
49 | static struct GNUNET_FS_Handle *fs; | ||
50 | |||
51 | static struct GNUNET_FS_UnindexContext *unindex; | ||
52 | |||
53 | static struct GNUNET_FS_PublishContext *publish; | ||
54 | |||
55 | static char *fn; | ||
56 | |||
57 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
58 | |||
59 | |||
60 | static void | ||
61 | abort_publish_task (void *cls) | ||
62 | { | ||
63 | GNUNET_FS_publish_stop (publish); | ||
64 | publish = NULL; | ||
65 | } | ||
66 | |||
67 | |||
68 | static void | ||
69 | abort_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 | |||
85 | static void * | ||
86 | progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event); | ||
87 | |||
88 | |||
89 | static void | ||
90 | restart_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 | */ | ||
105 | static void | ||
106 | consider_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 | |||
121 | static void * | ||
122 | progress_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 | |||
246 | static void | ||
247 | run (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_FS_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_FS_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_FS_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 | |||
296 | int | ||
297 | main (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 e0f23097b..000000000 --- a/src/fs/test_fs_uri.c +++ /dev/null | |||
@@ -1,340 +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 | |||
31 | static int | ||
32 | testKeyword () | ||
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 | |||
76 | static int | ||
77 | testLocation () | ||
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 | |||
150 | static int | ||
151 | testNamespace (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 | |||
231 | static int | ||
232 | testFile (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 | |||
312 | int | ||
313 | main (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_purge_cfg_dir | ||
331 | ("test_fs_uri.conf", | ||
332 | "GNUNET_TEST_HOME"); | ||
333 | |||
334 | if (failureCount != 0) | ||
335 | return 1; | ||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | |||
340 | /* 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) | ||
21 | import sys | ||
22 | import os | ||
23 | import subprocess | ||
24 | import re | ||
25 | import shutil | ||
26 | |||
27 | srcdir = "../.." | ||
28 | gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts") | ||
29 | if gnunet_pyexpect_dir not in sys.path: | ||
30 | sys.path.append(gnunet_pyexpect_dir) | ||
31 | |||
32 | from gnunet_pyexpect import pexpect | ||
33 | |||
34 | if os.name == 'posix': | ||
35 | download = './gnunet-download' | ||
36 | gnunetarm = 'gnunet-arm' | ||
37 | publish = './gnunet-publish' | ||
38 | unindex = './gnunet-unindex' | ||
39 | elif os.name == 'nt': | ||
40 | download = './gnunet-download.exe' | ||
41 | gnunetarm = 'gnunet-arm.exe' | ||
42 | publish = './gnunet-publish.exe' | ||
43 | unindex = './gnunet-unindex.exe' | ||
44 | |||
45 | if os.name == "nt": | ||
46 | shutil.rmtree( | ||
47 | os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-idx"), True | ||
48 | ) | ||
49 | else: | ||
50 | shutil.rmtree("/tmp/gnunet-test-fs-py-idx", True) | ||
51 | |||
52 | arm = subprocess.Popen([gnunetarm, '-sq', '-c', 'test_gnunet_fs_idx_data.conf']) | ||
53 | arm.communicate() | ||
54 | |||
55 | try: | ||
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 | |||
103 | finally: | ||
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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-py-idx/ | ||
4 | |||
5 | [transport] | ||
6 | PLUGINS = | ||
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 | |||
22 | import sys | ||
23 | import os | ||
24 | import subprocess | ||
25 | import re | ||
26 | import shutil | ||
27 | try: | ||
28 | # Python 2.7 | ||
29 | reload | ||
30 | except 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 | |||
38 | reload(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. | ||
43 | if (3 < sys.version_info[0]): | ||
44 | sys.setdefaultencoding('utf8') | ||
45 | |||
46 | srcdir = "../.." | ||
47 | gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts") | ||
48 | if gnunet_pyexpect_dir not in sys.path: | ||
49 | sys.path.append(gnunet_pyexpect_dir) | ||
50 | |||
51 | from gnunet_pyexpect import pexpect | ||
52 | |||
53 | if os.name == 'posix': | ||
54 | download = './gnunet-download' | ||
55 | gnunetarm = 'gnunet-arm' | ||
56 | publish = './gnunet-publish' | ||
57 | unindex = './gnunet-unindex' | ||
58 | search = './gnunet-search' | ||
59 | elif 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 | |||
66 | if "GNUNET_PREFIX" in os.environ: | ||
67 | pass | ||
68 | else: | ||
69 | print("You need to export GNUNET_PREFIX") | ||
70 | sys.exit(1) | ||
71 | |||
72 | if os.name == "nt": | ||
73 | shutil.rmtree( | ||
74 | os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-psd"), True | ||
75 | ) | ||
76 | else: | ||
77 | shutil.rmtree("/tmp/gnunet-test-fs-py-psd", True) | ||
78 | |||
79 | arm = subprocess.Popen([gnunetarm, '-sq', '-c', 'test_gnunet_fs_psd_data.conf']) | ||
80 | arm.communicate() | ||
81 | |||
82 | # first, basic publish-search-download run | ||
83 | try: | ||
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 | |||
139 | finally: | ||
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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-py-psd/ | ||
4 | |||
5 | [transport] | ||
6 | PLUGINS = | ||
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) | ||
21 | import sys | ||
22 | import os | ||
23 | import subprocess | ||
24 | import re | ||
25 | import shutil | ||
26 | import tarfile | ||
27 | import filecmp | ||
28 | |||
29 | srcdir = "../.." | ||
30 | gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts") | ||
31 | if gnunet_pyexpect_dir not in sys.path: | ||
32 | sys.path.append(gnunet_pyexpect_dir) | ||
33 | |||
34 | from gnunet_pyexpect import pexpect | ||
35 | from pydiffer import dcdiff | ||
36 | |||
37 | if 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' | ||
44 | elif 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 | |||
52 | if os.name == "nt": | ||
53 | shutil.rmtree( | ||
54 | os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-rec"), True | ||
55 | ) | ||
56 | else: | ||
57 | shutil.rmtree("/tmp/gnunet-test-fs-py-rec", True) | ||
58 | |||
59 | arm = subprocess.Popen([gnunetarm, '-sq', '-c', 'test_gnunet_fs_rec_data.conf']) | ||
60 | arm.communicate() | ||
61 | |||
62 | tar = tarfile.open('test_gnunet_fs_rec_data.tgz') | ||
63 | tar.extractall() | ||
64 | # first, basic publish-search-download run | ||
65 | try: | ||
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 | |||
158 | finally: | ||
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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-py-rec/ | ||
4 | |||
5 | [transport] | ||
6 | PLUGINS = | ||
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 38b00f3e8..000000000 --- a/src/fs/test_gnunet_service_fs_migration.c +++ /dev/null | |||
@@ -1,223 +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 | |||
50 | static struct GNUNET_TESTBED_Peer *daemons[2]; | ||
51 | |||
52 | static int ok; | ||
53 | |||
54 | static struct GNUNET_TIME_Absolute start_time; | ||
55 | |||
56 | static struct GNUNET_TESTBED_Operation *op; | ||
57 | |||
58 | |||
59 | struct DownloadContext | ||
60 | { | ||
61 | char *fn; | ||
62 | |||
63 | struct GNUNET_FS_Uri *uri; | ||
64 | }; | ||
65 | |||
66 | |||
67 | static void | ||
68 | do_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 | |||
101 | static void | ||
102 | do_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 | |||
142 | static void | ||
143 | stop_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 | |||
157 | static void | ||
158 | do_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 | |||
184 | static void | ||
185 | do_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 | |||
206 | int | ||
207 | main (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_purge_cfg_dir | ||
217 | ("test_gnunet_service_fs_migration_data.conf", | ||
218 | "GNUNET_TEST_HOME"); | ||
219 | return ok; | ||
220 | } | ||
221 | |||
222 | |||
223 | /* 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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-service-fs-migration/ | ||
4 | |||
5 | [testbed] | ||
6 | OVERLAY_TOPOLOGY = CLIQUE | ||
7 | |||
8 | [ats] | ||
9 | WAN_QUOTA_IN = 3932160 | ||
10 | WAN_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 2d1fbb788..000000000 --- a/src/fs/test_gnunet_service_fs_p2p.c +++ /dev/null | |||
@@ -1,167 +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 | |||
45 | static const char *progname; | ||
46 | |||
47 | static unsigned int anonymity_level; | ||
48 | |||
49 | static struct GNUNET_TESTBED_Peer *daemons[NUM_DAEMONS]; | ||
50 | |||
51 | static int ok; | ||
52 | |||
53 | static struct GNUNET_TIME_Absolute start_time; | ||
54 | |||
55 | |||
56 | static void | ||
57 | do_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 | |||
96 | static void | ||
97 | do_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 | |||
120 | static void | ||
121 | do_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 | |||
146 | int | ||
147 | main (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_purge_cfg_dir (config, | ||
162 | "GNUNET_TEST_HOME"); | ||
163 | return ok; | ||
164 | } | ||
165 | |||
166 | |||
167 | /* 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... | ||
8 | DISABLE_ANON_TRANSFER = YES | ||
9 | |||
10 | # Do we cache content from other nodes? (may improve anonymity) | ||
11 | CONTENT_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) | ||
15 | CONTENT_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 | |||
29 | static int | ||
30 | test_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 | |||
67 | int | ||
68 | main (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] | ||
5 | WEAKRANDOM = YES | ||
6 | |||