aboutsummaryrefslogtreecommitdiff
path: root/src/fs
diff options
context:
space:
mode:
authorMartin Schanzenbach <schanzen@gnunet.org>2023-10-19 11:55:21 +0200
committerMartin Schanzenbach <schanzen@gnunet.org>2023-10-19 11:55:21 +0200
commit579d9473bb75072303789599b23be9b0203336fc (patch)
tree687506d1968bd2a391b71b8832d1e97905db3ca8 /src/fs
parentb56e4e05ad919c7191260fcf1d78b1f8d739871a (diff)
downloadgnunet-579d9473bb75072303789599b23be9b0203336fc.tar.gz
gnunet-579d9473bb75072303789599b23be9b0203336fc.zip
BUILD: Move fs to contrib/service
Diffstat (limited to 'src/fs')
-rw-r--r--src/fs/.gitignore43
-rw-r--r--src/fs/Makefile.am531
-rw-r--r--src/fs/fs.conf.in63
-rw-r--r--src/fs/fs.h397
-rw-r--r--src/fs/fs_api.c3321
-rw-r--r--src/fs/fs_api.h1941
-rw-r--r--src/fs/fs_directory.c676
-rw-r--r--src/fs/fs_dirmetascan.c496
-rw-r--r--src/fs/fs_download.c2339
-rw-r--r--src/fs/fs_file_information.c407
-rw-r--r--src/fs/fs_getopt.c274
-rw-r--r--src/fs/fs_list_indexed.c219
-rw-r--r--src/fs/fs_misc.c165
-rw-r--r--src/fs/fs_namespace.c804
-rw-r--r--src/fs/fs_publish.c1625
-rw-r--r--src/fs/fs_publish_ksk.c255
-rw-r--r--src/fs/fs_publish_ublock.c299
-rw-r--r--src/fs/fs_publish_ublock.h109
-rw-r--r--src/fs/fs_search.c1826
-rw-r--r--src/fs/fs_sharetree.c457
-rw-r--r--src/fs/fs_test_lib.c630
-rw-r--r--src/fs/fs_test_lib.h102
-rw-r--r--src/fs/fs_test_lib_data.conf17
-rw-r--r--src/fs/fs_tree.c452
-rw-r--r--src/fs/fs_tree.h217
-rw-r--r--src/fs/fs_unindex.c902
-rw-r--r--src/fs/fs_uri.c2045
-rw-r--r--src/fs/gnunet-auto-share.c791
-rw-r--r--src/fs/gnunet-daemon-fsprofiler.c673
-rw-r--r--src/fs/gnunet-directory.c212
-rw-r--r--src/fs/gnunet-download.c385
-rw-r--r--src/fs/gnunet-fs-profiler.c245
-rw-r--r--src/fs/gnunet-fs.c191
-rw-r--r--src/fs/gnunet-helper-fs-publish.c579
-rw-r--r--src/fs/gnunet-publish.c1009
-rw-r--r--src/fs/gnunet-search.c801
-rw-r--r--src/fs/gnunet-service-fs.c1378
-rw-r--r--src/fs/gnunet-service-fs.h304
-rw-r--r--src/fs/gnunet-service-fs_cadet.h168
-rw-r--r--src/fs/gnunet-service-fs_cadet_client.c728
-rw-r--r--src/fs/gnunet-service-fs_cadet_server.c545
-rw-r--r--src/fs/gnunet-service-fs_cp.c1659
-rw-r--r--src/fs/gnunet-service-fs_cp.h415
-rw-r--r--src/fs/gnunet-service-fs_indexing.c495
-rw-r--r--src/fs/gnunet-service-fs_indexing.h120
-rw-r--r--src/fs/gnunet-service-fs_pe.c814
-rw-r--r--src/fs/gnunet-service-fs_pe.h95
-rw-r--r--src/fs/gnunet-service-fs_pr.c1887
-rw-r--r--src/fs/gnunet-service-fs_pr.h422
-rw-r--r--src/fs/gnunet-service-fs_push.c672
-rw-r--r--src/fs/gnunet-service-fs_push.h66
-rw-r--r--src/fs/gnunet-service-fs_put.c290
-rw-r--r--src/fs/gnunet-service-fs_put.h46
-rw-r--r--src/fs/gnunet-unindex.c206
-rw-r--r--src/fs/meson.build141
-rw-r--r--src/fs/meta_data.c1229
-rw-r--r--src/fs/perf_gnunet_service_fs_p2p.c370
-rw-r--r--src/fs/perf_gnunet_service_fs_p2p.conf7
-rw-r--r--src/fs/perf_gnunet_service_fs_p2p_respect.c480
-rw-r--r--src/fs/plugin_block_fs.c337
-rw-r--r--src/fs/test_fs.c262
-rw-r--r--src/fs/test_fs_data.conf7
-rw-r--r--src/fs/test_fs_defaults.conf47
-rw-r--r--src/fs/test_fs_directory.c186
-rw-r--r--src/fs/test_fs_download.c368
-rw-r--r--src/fs/test_fs_download_data.conf10
-rw-r--r--src/fs/test_fs_download_indexed.conf10
-rw-r--r--src/fs/test_fs_download_persistence.c351
-rw-r--r--src/fs/test_fs_file_information.c163
-rw-r--r--src/fs/test_fs_file_information_data.conf7
-rw-r--r--src/fs/test_fs_getopt.c37
-rw-r--r--src/fs/test_fs_list_indexed.c265
-rw-r--r--src/fs/test_fs_list_indexed_data.conf10
-rw-r--r--src/fs/test_fs_meta_data.c492
-rw-r--r--src/fs/test_fs_namespace.c320
-rw-r--r--src/fs/test_fs_namespace_data.conf7
-rw-r--r--src/fs/test_fs_namespace_list_updateable.c175
-rw-r--r--src/fs/test_fs_publish.c251
-rw-r--r--src/fs/test_fs_publish_data.conf10
-rw-r--r--src/fs/test_fs_publish_persistence.c322
-rw-r--r--src/fs/test_fs_search.c252
-rw-r--r--src/fs/test_fs_search_data.conf7
-rw-r--r--src/fs/test_fs_search_persistence.c318
-rw-r--r--src/fs/test_fs_search_probes.c258
-rw-r--r--src/fs/test_fs_search_with_and.c272
-rw-r--r--src/fs/test_fs_start_stop.c64
-rw-r--r--src/fs/test_fs_test_lib.c181
-rw-r--r--src/fs/test_fs_unindex.c237
-rw-r--r--src/fs/test_fs_unindex_data.conf7
-rw-r--r--src/fs/test_fs_unindex_persistence.c307
-rw-r--r--src/fs/test_fs_uri.c340
-rwxr-xr-xsrc/fs/test_gnunet_fs_idx.py.in113
-rw-r--r--src/fs/test_gnunet_fs_idx_data.conf7
-rwxr-xr-xsrc/fs/test_gnunet_fs_psd.py.in149
-rw-r--r--src/fs/test_gnunet_fs_psd_data.conf7
-rwxr-xr-xsrc/fs/test_gnunet_fs_rec.py.in171
-rw-r--r--src/fs/test_gnunet_fs_rec_data.conf7
-rw-r--r--src/fs/test_gnunet_fs_rec_data.tgzbin17822 -> 0 bytes
-rw-r--r--src/fs/test_gnunet_service_fs_migration.c223
-rw-r--r--src/fs/test_gnunet_service_fs_migration_data.conf10
-rw-r--r--src/fs/test_gnunet_service_fs_p2p.c167
-rw-r--r--src/fs/test_gnunet_service_fs_p2p_cadet.conf20
-rw-r--r--src/fs/test_plugin_block_fs.c86
-rw-r--r--src/fs/test_pseudonym_data.conf6
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 @@
1gnunet-unindex
2gnunet-auto-share
3gnunet-daemon-fsprofiler
4gnunet-directory
5gnunet-download
6gnunet-fs
7gnunet-fs-profiler
8gnunet-helper-fs-publish
9gnunet-publish
10gnunet-search
11gnunet-service-fs
12test_fs_directory
13test_fs_download
14test_fs_download_cadet
15test_fs_download_indexed
16test_fs_download_persistence
17test_fs_file_information
18test_fs_getopt
19test_fs_list_indexed
20test_fs_namespace
21test_fs_namespace_list_updateable
22test_fs_publish
23test_fs_publish_persistence
24test_fs_search
25test_fs_search_persistence
26test_fs_search_probes
27test_fs_search_with_and
28test_fs_start_stop
29test_fs_test_lib
30test_fs_unindex
31test_fs_unindex_persistence
32test_fs_uri
33test_gnunet_fs_idx.py
34test_gnunet_fs_psd.py
35test_gnunet_fs_rec.py
36test_gnunet_service_fs_migration
37test_gnunet_service_fs_p2p
38test_gnunet_service_fs_p2p_cadet
39test_plugin_block_fs
40perf_gnunet_service_fs_p2p
41perf_gnunet_service_fs_p2p_index
42perf_gnunet_service_fs_p2p_respect
43rdir.gnd
diff --git a/src/fs/Makefile.am b/src/fs/Makefile.am
deleted file mode 100644
index 38d75c0dc..000000000
--- a/src/fs/Makefile.am
+++ /dev/null
@@ -1,531 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9pkgcfgdir= $(pkgdatadir)/config.d/
10
11libexecdir= $(pkglibdir)/libexec/
12
13pkgcfg_DATA = \
14 fs.conf
15
16plugindir = $(libdir)/gnunet
17
18
19lib_LTLIBRARIES = libgnunetfs.la
20
21plugin_LTLIBRARIES = \
22 libgnunet_plugin_block_fs.la
23
24libgnunetfs_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
44libgnunetfs_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
50if HAVE_LIBEXTRACTOR
51libgnunetfs_la_LIBADD += \
52 -lextractor
53endif
54
55libgnunetfs_la_LDFLAGS = \
56 $(GN_LIB_LDFLAGS) \
57 -version-info 3:1:1
58
59
60libexec_PROGRAMS = \
61 gnunet-helper-fs-publish \
62 gnunet-service-fs
63
64noinst_PROGRAMS = \
65 gnunet-daemon-fsprofiler
66
67bin_PROGRAMS = \
68 gnunet-auto-share \
69 gnunet-directory \
70 gnunet-download \
71 gnunet-publish \
72 gnunet-search \
73 gnunet-fs \
74 gnunet-unindex
75
76gnunet_directory_SOURCES = \
77 gnunet-directory.c
78gnunet_directory_LDADD = \
79 libgnunetfs.la \
80 $(top_builddir)/src/lib/util/libgnunetutil.la \
81 $(GN_LIBINTL)
82
83if HAVE_LIBEXTRACTOR
84gnunet_directory_LDADD += \
85 -lextractor
86endif
87
88gnunet_fs_SOURCES = \
89 gnunet-fs.c
90gnunet_fs_LDADD = \
91 libgnunetfs.la \
92 $(top_builddir)/src/lib/util/libgnunetutil.la \
93 $(GN_LIBINTL)
94
95if HAVE_LIBEXTRACTOR
96gnunet_fs_LDADD += \
97 -lextractor
98endif
99
100gnunet_download_SOURCES = \
101 gnunet-download.c
102gnunet_download_LDADD = \
103 libgnunetfs.la \
104 $(top_builddir)/src/lib/util/libgnunetutil.la \
105 $(GN_LIBINTL)
106
107gnunet_publish_SOURCES = \
108 gnunet-publish.c
109gnunet_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
115if HAVE_LIBEXTRACTOR
116gnunet_publish_LDADD += \
117 -lextractor
118endif
119
120gnunet_auto_share_SOURCES = \
121 gnunet-auto-share.c
122gnunet_auto_share_LDADD = \
123 $(top_builddir)/src/lib/util/libgnunetutil.la \
124 $(GN_LIBINTL)
125
126if HAVE_LIBEXTRACTOR
127gnunet_auto_share_LDADD += \
128 -lextractor
129endif
130
131gnunet_helper_fs_publish_SOURCES = \
132 gnunet-helper-fs-publish.c
133gnunet_helper_fs_publish_LDADD = \
134 libgnunetfs.la \
135 $(top_builddir)/src/lib/util/libgnunetutil.la \
136 $(GN_LIBINTL)
137
138if HAVE_LIBEXTRACTOR
139gnunet_helper_fs_publish_LDADD += \
140 -lextractor
141endif
142
143gnunet_search_SOURCES = \
144 gnunet-search.c
145gnunet_search_LDADD = \
146 libgnunetfs.la \
147 $(top_builddir)/src/lib/util/libgnunetutil.la \
148 $(GN_LIBINTL)
149
150if HAVE_LIBEXTRACTOR
151gnunet_search_LDADD += \
152 -lextractor
153endif
154
155
156gnunet_daemon_fsprofiler_SOURCES = \
157 gnunet-daemon-fsprofiler.c
158gnunet_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
164gnunet_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
174gnunet_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
186gnunet_unindex_SOURCES = \
187 gnunet-unindex.c
188gnunet_unindex_LDADD = \
189 libgnunetfs.la \
190 $(top_builddir)/src/lib/util/libgnunetutil.la \
191 $(GN_LIBINTL)
192
193libgnunet_plugin_block_fs_la_SOURCES = \
194 plugin_block_fs.c
195libgnunet_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)
201libgnunet_plugin_block_fs_la_LDFLAGS = \
202 $(GN_PLUGIN_LDFLAGS)
203
204check_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
231test_plugin_block_fs_SOURCES = \
232 test_plugin_block_fs.c
233test_plugin_block_fs_LDADD = \
234 $(top_builddir)/src/lib/block/libgnunetblock.la \
235 $(top_builddir)/src/lib/util/libgnunetutil.la
236
237if HAVE_PYTHON
238check_SCRIPTS = \
239 test_gnunet_fs_rec.py \
240 test_gnunet_fs_idx.py
241
242if HAVE_LIBEXTRACTOR
243check_SCRIPTS += \
244 test_gnunet_fs_psd.py
245endif
246endif
247
248
249if ENABLE_TEST_RUN
250AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
251TESTS = 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)
263endif
264
265
266test_fs_directory_SOURCES = \
267 test_fs_directory.c
268test_fs_directory_LDADD = \
269 libgnunetfs.la \
270 $(top_builddir)/src/lib/util/libgnunetutil.la
271
272if HAVE_LIBEXTRACTOR
273test_fs_directory_LDADD += \
274 -lextractor
275endif
276
277
278test_fs_download_SOURCES = \
279 test_fs_download.c
280test_fs_download_LDADD = \
281 $(top_builddir)/src/service/testing/libgnunettesting.la \
282 libgnunetfs.la \
283 $(top_builddir)/src/lib/util/libgnunetutil.la
284
285test_fs_download_indexed_SOURCES = \
286 test_fs_download.c
287test_fs_download_indexed_LDADD = \
288 $(top_builddir)/src/service/testing/libgnunettesting.la \
289 libgnunetfs.la \
290 $(top_builddir)/src/lib/util/libgnunetutil.la
291
292test_fs_download_cadet_SOURCES = \
293 test_fs_download.c
294test_fs_download_cadet_LDADD = \
295 $(top_builddir)/src/service/testing/libgnunettesting.la \
296 libgnunetfs.la \
297 $(top_builddir)/src/lib/util/libgnunetutil.la
298
299test_fs_download_persistence_SOURCES = \
300 test_fs_download_persistence.c
301test_fs_download_persistence_LDADD = \
302 $(top_builddir)/src/service/testing/libgnunettesting.la \
303 libgnunetfs.la \
304 $(top_builddir)/src/lib/util/libgnunetutil.la
305
306test_fs_file_information_SOURCES = \
307 test_fs_file_information.c
308test_fs_file_information_LDADD = \
309 libgnunetfs.la \
310 $(top_builddir)/src/lib/util/libgnunetutil.la
311
312if HAVE_LIBEXTRACTOR
313test_fs_file_information_LDADD += \
314 -lextractor
315endif
316
317
318test_fs_getopt_SOURCES = \
319 test_fs_getopt.c
320test_fs_getopt_LDADD = \
321 libgnunetfs.la \
322 $(top_builddir)/src/lib/util/libgnunetutil.la
323
324test_fs_list_indexed_SOURCES = \
325 test_fs_list_indexed.c
326test_fs_list_indexed_LDADD = \
327 $(top_builddir)/src/service/testing/libgnunettesting.la \
328 libgnunetfs.la \
329 $(top_builddir)/src/lib/util/libgnunetutil.la
330
331test_fs_namespace_SOURCES = \
332 test_fs_namespace.c
333test_fs_namespace_LDADD = \
334 $(top_builddir)/src/service/testing/libgnunettesting.la \
335 libgnunetfs.la \
336 $(top_builddir)/src/lib/util/libgnunetutil.la
337
338test_fs_namespace_list_updateable_SOURCES = \
339 test_fs_namespace_list_updateable.c
340test_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
345test_fs_publish_SOURCES = \
346 test_fs_publish.c
347test_fs_publish_LDADD = \
348 $(top_builddir)/src/service/testing/libgnunettesting.la \
349 libgnunetfs.la \
350 $(top_builddir)/src/lib/util/libgnunetutil.la
351
352test_fs_publish_persistence_SOURCES = \
353 test_fs_publish_persistence.c
354test_fs_publish_persistence_LDADD = \
355 $(top_builddir)/src/service/testing/libgnunettesting.la \
356 libgnunetfs.la \
357 $(top_builddir)/src/lib/util/libgnunetutil.la
358
359test_fs_search_SOURCES = \
360 test_fs_search.c
361test_fs_search_LDADD = \
362 $(top_builddir)/src/service/testing/libgnunettesting.la \
363 libgnunetfs.la \
364 $(top_builddir)/src/lib/util/libgnunetutil.la
365
366test_fs_search_with_and_SOURCES = \
367 test_fs_search_with_and.c
368test_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
373test_fs_search_probes_SOURCES = \
374 test_fs_search_probes.c
375test_fs_search_probes_LDADD = \
376 $(top_builddir)/src/service/testing/libgnunettesting.la \
377 libgnunetfs.la \
378 $(top_builddir)/src/lib/util/libgnunetutil.la
379
380test_fs_search_persistence_SOURCES = \
381 test_fs_search_persistence.c
382test_fs_search_persistence_LDADD = \
383 $(top_builddir)/src/service/testing/libgnunettesting.la \
384 libgnunetfs.la \
385 $(top_builddir)/src/lib/util/libgnunetutil.la
386
387test_fs_start_stop_SOURCES = \
388 test_fs_start_stop.c
389test_fs_start_stop_LDADD = \
390 $(top_builddir)/src/service/testing/libgnunettesting.la \
391 libgnunetfs.la \
392 $(top_builddir)/src/lib/util/libgnunetutil.la
393
394test_fs_unindex_SOURCES = \
395 test_fs_unindex.c
396test_fs_unindex_LDADD = \
397 $(top_builddir)/src/service/testing/libgnunettesting.la \
398 libgnunetfs.la \
399 $(top_builddir)/src/lib/util/libgnunetutil.la
400
401test_fs_unindex_persistence_SOURCES = \
402 test_fs_unindex_persistence.c
403test_fs_unindex_persistence_LDADD = \
404 $(top_builddir)/src/service/testing/libgnunettesting.la \
405 libgnunetfs.la \
406 $(top_builddir)/src/lib/util/libgnunetutil.la
407
408test_fs_meta_data_SOURCES = \
409 test_fs_meta_data.c
410test_fs_meta_data_LDADD = \
411 libgnunetfs.la \
412 $(top_builddir)/src/lib/util/libgnunetutil.la
413
414
415test_fs_uri_SOURCES = \
416 test_fs_uri.c
417test_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
490test_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
494test_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
498test_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
502test_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
507EXTRA_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
531CLEANFILES = $(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]
2RUN_PER_USER = YES
3START_ON_DEMAND = @START_ON_DEMAND@
4IMMEDIATE_START = YES
5INDEXDB = $GNUNET_DATA_HOME/fs/idxinfo.lst
6RESPECT = $GNUNET_DATA_HOME/fs/credit/
7STATE_DIR = $GNUNET_DATA_HOME/fs/persistence/
8UPDATE_DIR = $GNUNET_DATA_HOME/fs/updates/
9@UNIXONLY@ PORT = 2094
10HOSTNAME = localhost
11BINARY = gnunet-service-fs
12ACCEPT_FROM = 127.0.0.1;
13ACCEPT_FROM6 = ::1;
14
15# PREFIX = valgrind
16
17# Do we introduce artificial delays? (may improve anonymity)
18DELAY = YES
19
20# Do we cache content from other nodes? (may improve anonymity)
21CONTENT_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)
25CONTENT_PUSHING = YES
26
27UNIXPATH = $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)
31UNIX_MATCH_UID = NO
32
33# Do we require users that want to access file-sharing to be in the 'gnunet' group?
34UNIX_MATCH_GID = YES
35
36# Maximum number of requests this peer tracks (important for
37# memory consumption; 2k RAM/request is not unusual)
38MAX_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)
42DATASTORE_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)
47MIN_MIGRATION_DELAY = 100 ms
48
49# For how many neighbouring peers should we allocate hash maps?
50EXPECTED_NEIGHBOUR_COUNT = 128
51
52# Disable anonymous file-sharing (but keep non-anonymous transfers)?
53# This option is mostly for testing.
54DISABLE_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.
62MAX_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 */
54struct 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
68GNUNET_NETWORK_STRUCT_BEGIN
69
70
71/**
72 * Message sent from a GNUnet (fs) publishing activity to sign
73 * a LOC URI.
74 */
75struct 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 */
108struct 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 */
152struct 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 */
196struct 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 */
227struct 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 */
269struct 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 */
329struct 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 */
355struct 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};
392GNUNET_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 */
50static void
51start_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 */
77static void
78stop_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 */
106static void
107process_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
320struct GNUNET_FS_QueueEntry *
321GNUNET_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 */
355void
356GNUNET_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 */
380struct TopLevelActivity *
381GNUNET_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 */
401void
402GNUNET_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 */
412struct 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 */
446size_t
447GNUNET_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
508void *
509GNUNET_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 */
544size_t
545GNUNET_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 */
574static char *
575get_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 */
614static char *
615get_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 */
654static struct GNUNET_BIO_ReadHandle *
655get_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 */
677static struct GNUNET_BIO_WriteHandle *
678get_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 */
702static struct GNUNET_BIO_WriteHandle *
703get_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 */
727void
728GNUNET_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 */
757static void
758remove_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 */
786void
787GNUNET_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 */
819static int
820write_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 */
844static int
845read_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 */
867static struct GNUNET_FS_FileInformation *
868deserialize_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 */
881static struct GNUNET_FS_FileInformation *
882deserialize_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;
1123cleanup:
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 */
1142static struct GNUNET_FS_FileInformation *
1143deserialize_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 */
1185static char *
1186get_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 */
1219static char *
1220make_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 */
1255static char *
1256make_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 */
1291static int
1292copy_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 */
1327void
1328GNUNET_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! */
1511cleanup:
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 */
1539static struct GNUNET_FS_FileInformation *
1540find_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 */
1571static int
1572fip_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 */
1611static int
1612deserialize_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;
1715cleanup:
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 */
1746void
1747GNUNET_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;
1803cleanup:
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 */
1822void
1823GNUNET_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;
1879cleanup:
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 */
1897static int
1898write_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 */
1930static struct DownloadRequest *
1931read_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;
1987cleanup:
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 */
2002static char *
2003get_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 */
2035void
2036GNUNET_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;
2128cleanup:
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 */
2148void
2149GNUNET_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;
2224cleanup:
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 */
2247void
2248GNUNET_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;
2293cleanup:
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 */
2311static int
2312deserialize_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;
2458cleanup:
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 */
2487static void
2488deserialize_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 */
2503static struct GNUNET_FS_SearchContext *
2504deserialize_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 */
2518static int
2519deserialize_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;
2651cleanup:
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 */
2682static void
2683signal_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 */
2707static void
2708signal_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 */
2720static int
2721signal_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 */
2760static void
2761free_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 */
2772static int
2773free_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 */
2794static void
2795free_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 */
2833static int
2834deserialize_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 */
2874static void
2875free_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 */
2908static void
2909deserialize_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;
3006cleanup:
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 */
3019static void
3020signal_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 */
3043static struct GNUNET_FS_SearchContext *
3044deserialize_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;
3113cleanup:
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 */
3129static int
3130deserialize_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 */
3181static int
3182deserialize_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 */
3223static void
3224deserialization_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
3243struct GNUNET_FS_Handle *
3244GNUNET_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
3309void
3310GNUNET_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 */
94struct 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 */
112struct 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 */
139enum 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 */
166struct 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 */
227struct 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 */
403enum 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 */
420struct 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 */
498struct 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 */
628struct GNUNET_FS_QueueEntry *
629GNUNET_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 */
642void
643GNUNET_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 */
662size_t
663GNUNET_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 */
676void *
677GNUNET_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 */
696size_t
697GNUNET_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 */
719void *
720GNUNET_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 */
729void
730GNUNET_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 */
740void
741GNUNET_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 */
750void
751GNUNET_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 */
759void
760GNUNET_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 */
773void *
774GNUNET_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 */
787void
788GNUNET_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 */
798void
799GNUNET_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 */
810void
811GNUNET_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 */
824void *
825GNUNET_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 */
835void
836GNUNET_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 */
845int
846GNUNET_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 */
853void
854GNUNET_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 */
862void
863GNUNET_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 */
873void
874GNUNET_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 */
886void
887GNUNET_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 */
900void
901GNUNET_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 */
912void
913GNUNET_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 */
924void
925GNUNET_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 */
936void
937GNUNET_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 */
948void
949GNUNET_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 */
960void
961GNUNET_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 */
970void
971GNUNET_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 */
980void
981GNUNET_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 */
990void
991GNUNET_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 */
1000void
1001GNUNET_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 */
1011typedef 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 */
1017struct 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 */
1049struct TopLevelActivity *
1050GNUNET_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 */
1061void
1062GNUNET_FS_end_top (struct GNUNET_FS_Handle *h,
1063 struct TopLevelActivity *top);
1064
1065
1066/**
1067 * Master context for most FS operations.
1068 */
1069struct 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 */
1179struct 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 */
1306enum 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 */
1350struct 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 */
1471struct 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 */
1510struct 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 */
1607enum 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 */
1675struct 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 */
1727void
1728GNUNET_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 */
1736void
1737GNUNET_FS_stop_probe_ping_task_ (struct GNUNET_FS_SearchResult *sr);
1738
1739
1740/**
1741 * Context for controlling a download.
1742 */
1743struct 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 */
54int
55GNUNET_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 */
80void
81GNUNET_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 */
104struct 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. '&lt;zlib&gt;' 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 */
135static int
136find_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 */
181int
182GNUNET_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 */
325struct 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 */
341struct 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 */
365struct GNUNET_FS_DirectoryBuilder *
366GNUNET_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 */
391void
392GNUNET_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 */
487static size_t
488do_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 */
508static void
509block_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 */
577int
578GNUNET_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 */
39struct 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 */
98void
99GNUNET_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
116struct GNUNET_FS_ShareTreeItem *
117GNUNET_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 */
138static struct GNUNET_FS_ShareTreeItem *
139advance (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 */
180static struct GNUNET_FS_ShareTreeItem *
181expand_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 */
214static void
215finish_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 */
242static int
243process_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 */
423static void
424helper_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 */
449struct GNUNET_FS_DirScanner *
450GNUNET_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 */
37static int
38is_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 */
67static uint64_t
68compute_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 */
103void
104GNUNET_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 */
135struct 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 */
193static int
194process_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 */
212static int
213encrypt_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 */
265static void
266try_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 */
280static void
281trigger_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 */
295static void
296full_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 */
375static void
376check_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 */
431static void
432try_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. '&lt;zlib&gt;' 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 */
601static int
602match_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 */
633static void
634propagate_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 */
662static void
663try_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 */
746static int
747retry_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 */
777static void
778schedule_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 */
850static void
851trigger_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 */
996void
997GNUNET_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
1008static int
1009process_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
1264signal_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 */
1291static int
1292check_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 */
1306static void
1307handle_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 */
1340static void
1341download_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 */
1361static void
1362do_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 */
1397static void
1398try_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 */
1427static void
1428activate_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 */
1448static void
1449deactivate_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 */
1484static struct DownloadRequest *
1485create_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 */
1560static void
1561reconstruct_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 */
1588static void
1589get_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 */
1615static void
1616reconstruct_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 */
1767static size_t
1768fh_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 */
1799void
1800GNUNET_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
1952void
1953GNUNET_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 */
2028struct GNUNET_FS_DownloadContext *
2029create_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
2099struct GNUNET_FS_DownloadContext *
2100GNUNET_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
2136struct GNUNET_FS_DownloadContext *
2137GNUNET_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 */
2183void
2184GNUNET_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 */
2214void
2215GNUNET_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 */
2226void
2227GNUNET_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 */
2253void
2254GNUNET_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 */
44const char *
45GNUNET_FS_file_information_get_id (struct GNUNET_FS_FileInformation *s)
46{
47 if (NULL != s->dir)
48 return NULL;
49 return s->serialization;
50}
51
52
53/**
54 * Obtain the filename from the file information structure.
55 *
56 * @param s structure to get the filename for
57 * @return "filename" field of the structure (can be NULL)
58 */
59const char *
60GNUNET_FS_file_information_get_filename (const struct
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 */
75void
76GNUNET_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
87struct GNUNET_FS_FileInformation *
88GNUNET_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
147struct GNUNET_FS_FileInformation *
148GNUNET_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
176struct GNUNET_FS_FileInformation *
177GNUNET_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 */
217int
218GNUNET_FS_file_information_is_directory (
219 const struct GNUNET_FS_FileInformation *ent)
220{
221 return ent->is_directory;
222}
223
224
225struct GNUNET_FS_FileInformation *
226GNUNET_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
249int
250GNUNET_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 */
281void
282GNUNET_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 */
334void
335GNUNET_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 */
45static int
46getopt_set_keywords (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
47 void *scls,
48 const char *option,
49 const char *value)
50{
51 struct GNUNET_FS_Uri **uri = scls;
52 struct GNUNET_FS_Uri *u = *uri;
53 char *val;
54 size_t slen;
55
56 if (NULL == u)
57 {
58 u = GNUNET_new (struct GNUNET_FS_Uri);
59 *uri = u;
60 u->type = GNUNET_FS_URI_KSK;
61 u->data.ksk.keywordCount = 0;
62 u->data.ksk.keywords = NULL;
63 }
64 else
65 {
66 GNUNET_assert (GNUNET_FS_URI_KSK == u->type);
67 }
68 slen = strlen (value);
69 if (0 == slen)
70 return GNUNET_SYSERR; /* cannot be empty */
71 if (value[0] == '+')
72 {
73 /* simply preserve the "mandatory" flag */
74 if (slen < 2)
75 return GNUNET_SYSERR; /* empty keywords not allowed */
76 if ((value[1] == '"') && (slen > 3) && (value[slen - 1] == '"'))
77 {
78 /* remove the quotes, keep the '+' */
79 val = GNUNET_malloc (slen - 1);
80 val[0] = '+';
81 GNUNET_memcpy (&val[1],
82 &value[2],
83 slen - 3);
84 val[slen - 2] = '\0';
85 }
86 else
87 {
88 /* no quotes, just keep the '+' */
89 val = GNUNET_strdup (value);
90 }
91 }
92 else
93 {
94 if ((value[0] == '"') && (slen > 2) && (value[slen - 1] == '"'))
95 {
96 /* remove the quotes, add a space */
97 val = GNUNET_malloc (slen);
98 val[0] = ' ';
99 GNUNET_memcpy (&val[1],
100 &value[1],
101 slen - 2);
102 val[slen - 1] = '\0';
103 }
104 else
105 {
106 /* add a space to indicate "not mandatory" */
107 val = GNUNET_malloc (slen + 2);
108 strcpy (val, " ");
109 strcat (val, value);
110 }
111 }
112 GNUNET_array_append (u->data.ksk.keywords,
113 u->data.ksk.keywordCount,
114 val);
115 return GNUNET_OK;
116}
117
118
119/**
120 * Allow user to specify keywords.
121 *
122 * @param shortName short name of the option
123 * @param name long name of the option
124 * @param argumentHelp help text for the option argument
125 * @param description long help text for the option
126 * @param[out] topKeywords set to the desired value
127 */
128struct GNUNET_GETOPT_CommandLineOption
129GNUNET_FS_GETOPT_KEYWORDS (char shortName,
130 const char *name,
131 const char *argumentHelp,
132 const char *description,
133 struct GNUNET_FS_Uri **topKeywords)
134{
135 struct GNUNET_GETOPT_CommandLineOption clo = {
136 .shortName = shortName,
137 .name = name,
138 .argumentHelp = argumentHelp,
139 .description = description,
140 .require_argument = 1,
141 .processor = &getopt_set_keywords,
142 .scls = (void *) topKeywords
143 };
144
145 return clo;
146}
147
148
149/**
150 * Command-line option parser function that allows the user to specify
151 * one or more '-m' options with metadata. Each specified entry of
152 * the form "type=value" will be added to the metadata. A pointer to
153 * the metadata must be passed as the "scls" argument.
154 *
155 * @param ctx command line processor context
156 * @param scls must be of type "struct GNUNET_MetaData **"
157 * @param option name of the option (typically 'k')
158 * @param value command line argument given
159 * @return #GNUNET_OK on success
160 */
161static int
162getopt_set_metadata (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
163 void *scls,
164 const char *option,
165 const char *value)
166{
167 struct GNUNET_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 */
253struct GNUNET_GETOPT_CommandLineOption
254GNUNET_FS_GETOPT_METADATA (char shortName,
255 const char *name,
256 const char *argumentHelp,
257 const char *description,
258 struct GNUNET_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 */
38struct 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 */
74static void
75handle_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 */
94static int
95check_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 */
118static void
119handle_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 */
145static void
146mq_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
162struct GNUNET_FS_GetIndexedContext *
163GNUNET_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 */
211void
212GNUNET_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 */
38char *
39GNUNET_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 */
41struct 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 */
79struct 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 */
127static char *
128get_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 */
162static void
163free_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 */
189static void
190write_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 */
248static struct GNUNET_FS_UpdateInformationGraph *
249read_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 */
344struct 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 */
396static void
397sks_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
427struct GNUNET_FS_PublishSksContext *
428GNUNET_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 */
490void
491GNUNET_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 */
519struct 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 */
543static int
544process_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 */
557struct 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 */
606static int
607find_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 */
666void
667GNUNET_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 */
47void *
48GNUNET_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 */
78static void
79publish_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 */
116static void
117ds_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 */
164static void
165signal_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 */
189static void
190signal_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 */
222static void
223finish_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 */
246static void
247publish_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 */
285static void
286publish_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 */
312static void
313publish_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 */
367static size_t
368block_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 */
417static void
418encode_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 */
481static void
482block_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 */
566static void
567progress_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 */
615static void
616publish_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 */
696static int
697check_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 */
719static void
720handle_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 */
751static void
752handle_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 */
775static void
776index_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 */
805static void
806hash_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 */
933static void
934publish_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 */
964static void
965handle_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 */
992static void
993loc_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 */
1016static void
1017create_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 */
1062void
1063GNUNET_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 */
1193static int
1194fip_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 */
1266static void
1267suspend_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 */
1319static int
1320fip_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 */
1354void
1355GNUNET_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 */
1382static void
1383finish_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 */
1415static uint64_t
1416compute_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 */
1442struct GNUNET_FS_PublishContext *
1443GNUNET_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 */
1523static int
1524fip_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 */
1570void
1571GNUNET_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 */
42struct 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 */
113static void
114publish_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 */
124static void
125kb_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
143static void
144publish_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 */
187struct GNUNET_FS_PublishKskContext *
188GNUNET_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 */
230void
231GNUNET_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 */
45static void
46derive_ublock_encryption_key (struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
47 struct GNUNET_CRYPTO_SymmetricInitializationVector
48 *iv,
49 const char *label,
50 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
51{
52 struct GNUNET_HashCode key;
53
54 /* derive key from 'label' and public key of the namespace */
55 GNUNET_assert (GNUNET_YES ==
56 GNUNET_CRYPTO_kdf (&key, sizeof(key),
57 "UBLOCK-ENC", strlen ("UBLOCK-ENC"),
58 label, strlen (label),
59 pub, sizeof(*pub),
60 NULL, 0));
61 GNUNET_CRYPTO_hash_to_aes_key (&key, skey, iv);
62}
63
64
65void
66GNUNET_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 */
86struct 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 */
122static void
123ublock_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 */
141static void
142run_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
152struct GNUNET_FS_PublishUblockContext *
153GNUNET_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 */
288void
289GNUNET_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 */
47void
48GNUNET_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 */
58struct 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 */
68typedef 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 */
88struct GNUNET_FS_PublishUblockContext *
89GNUNET_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 */
106void
107GNUNET_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 */
48void *
49GNUNET_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 */
83static int
84test_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 */
104static void
105notify_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 */
126static void
127notify_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 */
150struct 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 */
175static int
176get_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 */
195static void
196signal_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 */
221static void
222probe_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 */
245static void
246probe_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 */
280void *
281GNUNET_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 */
367static void
368probe_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 */
389static void
390start_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 */
409void
410GNUNET_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 */
430void
431GNUNET_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 */
488struct GNUNET_FS_SearchResult *
489GNUNET_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 */
515static void
516GNUNET_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 */
539void *
540GNUNET_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 */
564static void
565process_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 */
667static struct GNUNET_FS_SearchContext *
668search_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 */
685static void
686process_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 */
753static int
754decrypt_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 */
796static void
797process_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 */
868static void
869process_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 */
923static void
924try_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 */
933static int
934check_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 */
948static void
949handle_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 */
1007static void
1008schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc);
1009
1010
1011/**
1012 * Closure for #build_result_set().
1013 */
1014struct 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 */
1052static int
1053build_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 */
1088static int
1089find_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 */
1113static void
1114schedule_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 */
1250static void
1251search_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 */
1271static void
1272do_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 */
1305static void
1306try_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 */
1333static struct GNUNET_FS_SearchContext *
1334search_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 */
1380static int
1381update_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
1401int
1402GNUNET_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 */
1461static int
1462search_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 */
1493static int
1494search_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 */
1515static int
1516search_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
1554void
1555GNUNET_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 */
1606struct GNUNET_FS_SearchContext *
1607GNUNET_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 */
1626void
1627GNUNET_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 */
1657void
1658GNUNET_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 */
1681static int
1682search_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 */
1736static int
1737search_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 */
1765void
1766GNUNET_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 */
38struct 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 */
65struct 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 */
119struct 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 */
155static int
156add_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. '&lt;zlib&gt;' 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 */
198static int
199add_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 */
238static int
239remove_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 */
268static int
269migrate_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 */
302static int
303migrate_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 */
336static void
337share_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 */
416void
417GNUNET_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 */
436void
437GNUNET_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 */
40struct TestPublishOperation
41{
42 /**
43 * Handle for the operation to connect to the peer's 'fs' service.
44 */
45 struct GNUNET_TESTBED_Operation *fs_op;
46
47 /**
48 * Handle to the file sharing context using this daemon.
49 */
50 struct GNUNET_FS_Handle *fs;
51
52 /**
53 * Function to call when upload is done.
54 */
55 GNUNET_FS_TEST_UriContinuation publish_cont;
56
57 /**
58 * Closure for publish_cont.
59 */
60 void *publish_cont_cls;
61
62 /**
63 * Task to abort publishing (timeout).
64 */
65 struct GNUNET_SCHEDULER_Task *publish_timeout_task;
66
67 /**
68 * Seed for file generation.
69 */
70 uint32_t publish_seed;
71
72 /**
73 * Context for current publishing operation.
74 */
75 struct GNUNET_FS_PublishContext *publish_context;
76
77 /**
78 * Result URI.
79 */
80 struct GNUNET_FS_Uri *publish_uri;
81
82 /**
83 * Name of the temporary file used, or NULL for none.
84 */
85 char *publish_tmp_file;
86
87 /**
88 * Size of the file.
89 */
90 uint64_t size;
91
92 /**
93 * Anonymity level used.
94 */
95 uint32_t anonymity;
96
97 /**
98 * Verbosity level of the current operation.
99 */
100 unsigned int verbose;
101
102 /**
103 * Are we testing indexing? (YES: index, NO: insert, SYSERR: simulate)
104 */
105 int do_index;
106};
107
108
109/**
110 * Handle for a download operation started for testing FS.
111 */
112struct TestDownloadOperation
113{
114 /**
115 * Handle for the operation to connect to the peer's 'fs' service.
116 */
117 struct GNUNET_TESTBED_Operation *fs_op;
118
119 /**
120 * Handle to the file sharing context using this daemon.
121 */
122 struct GNUNET_FS_Handle *fs;
123
124 /**
125 * Handle to the daemon via testing.
126 */
127 struct GNUNET_TESTING_Daemon *daemon;
128
129 /**
130 * Function to call when download is done.
131 */
132 GNUNET_SCHEDULER_TaskCallback download_cont;
133
134 /**
135 * Closure for download_cont.
136 */
137 void *download_cont_cls;
138
139 /**
140 * URI to download.
141 */
142 struct GNUNET_FS_Uri *uri;
143
144 /**
145 * Task to abort downloading (timeout).
146 */
147 struct GNUNET_SCHEDULER_Task *download_timeout_task;
148
149 /**
150 * Context for current download operation.
151 */
152 struct GNUNET_FS_DownloadContext *download_context;
153
154 /**
155 * Size of the file.
156 */
157 uint64_t size;
158
159 /**
160 * Anonymity level used.
161 */
162 uint32_t anonymity;
163
164 /**
165 * Seed for download verification.
166 */
167 uint32_t download_seed;
168
169 /**
170 * Verbosity level of the current operation.
171 */
172 unsigned int verbose;
173};
174
175
176/**
177 * Task scheduled to report on the completion of our publish operation.
178 *
179 * @param cls the publish operation context
180 */
181static void
182report_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 */
207static void
208publish_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 */
230static void *
231publish_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 */
280static size_t
281file_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 */
318static void *
319publish_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 */
338static void
339fs_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 */
357static void
358publish_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
432void
433GNUNET_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 */
470static void
471download_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 */
493static void
494report_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 */
514static void *
515download_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 */
559static void *
560download_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 */
582static void
583download_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
599void
600GNUNET_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 */
45typedef void
46(*GNUNET_FS_TEST_UriContinuation) (void *cls,
47 const struct GNUNET_FS_Uri *uri,
48 const char *fn);
49
50
51/**
52 * Publish a file at the given daemon.
53 *
54 * @param peer where to publish
55 * @param timeout if this operation cannot be completed within the
56 * given period, call the continuation with an error code
57 * @param anonymity option for publication
58 * @param do_index #GNUNET_YES for index, #GNUNET_NO for insertion,
59 * #GNUNET_SYSERR for simulation
60 * @param size size of the file to publish
61 * @param seed seed to use for file generation
62 * @param verbose how verbose to be in reporting
63 * @param cont function to call when done
64 * @param cont_cls closure for @a cont
65 */
66void
67GNUNET_FS_TEST_publish (struct GNUNET_TESTBED_Peer *peer,
68 struct GNUNET_TIME_Relative timeout,
69 uint32_t anonymity,
70 int do_index,
71 uint64_t size,
72 uint32_t seed,
73 unsigned int verbose,
74 GNUNET_FS_TEST_UriContinuation cont,
75 void *cont_cls);
76
77
78/**
79 * Perform test download.
80 *
81 * @param peer which peer to download from
82 * @param timeout if this operation cannot be completed within the
83 * given period, call the continuation with an error code
84 * @param anonymity option for download
85 * @param seed used for file validation
86 * @param uri URI of file to download (CHK/LOC only)
87 * @param verbose how verbose to be in reporting
88 * @param cont function to call when done
89 * @param cont_cls closure for @a cont
90 */
91void
92GNUNET_FS_TEST_download (struct GNUNET_TESTBED_Peer *peer,
93 struct GNUNET_TIME_Relative timeout,
94 uint32_t anonymity,
95 uint32_t seed,
96 const struct GNUNET_FS_Uri *uri,
97 unsigned int verbose,
98 GNUNET_SCHEDULER_TaskCallback cont,
99 void *cont_cls);
100
101
102#endif
diff --git a/src/fs/fs_test_lib_data.conf b/src/fs/fs_test_lib_data.conf
deleted file mode 100644
index c99674798..000000000
--- a/src/fs/fs_test_lib_data.conf
+++ /dev/null
@@ -1,17 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-fs-test-lib/
4
5[ats]
6WAN_QUOTA_IN = 3932160
7WAN_QUOTA_OUT = 3932160
8
9[datastore]
10QUOTA = 2 GB
11#PLUGIN = heap
12#
13[fs]
14IMMEDIATE_START = YES
15
16[testbed]
17OVERLAY_TOPOLOGY = CLIQUE
diff --git a/src/fs/fs_tree.c b/src/fs/fs_tree.c
deleted file mode 100644
index 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 */
35struct GNUNET_FS_TreeEncoder
36{
37 /**
38 * Global FS context.
39 */
40 struct GNUNET_FS_Handle *h;
41
42 /**
43 * Closure for all callbacks.
44 */
45 void *cls;
46
47 /**
48 * Function to call on encrypted blocks.
49 */
50 GNUNET_FS_TreeBlockProcessor proc;
51
52 /**
53 * Function to call with progress information.
54 */
55 GNUNET_FS_TreeProgressCallback progress;
56
57 /**
58 * Function to call to receive input data.
59 */
60 GNUNET_FS_DataReader reader;
61
62 /**
63 * Function to call once we're done with processing.
64 */
65 GNUNET_SCHEDULER_TaskCallback cont;
66
67 /**
68 * Set to an error message (if we had an error).
69 */
70 char *emsg;
71
72 /**
73 * Set to the URI (upon successful completion)
74 */
75 struct GNUNET_FS_Uri *uri;
76
77 /**
78 * Overall file size.
79 */
80 uint64_t size;
81
82 /**
83 * How far are we?
84 */
85 uint64_t publish_offset;
86
87 /**
88 * How deep are we? Depth 0 is for the DBLOCKs.
89 */
90 unsigned int current_depth;
91
92 /**
93 * How deep is the tree? Always > 0.
94 */
95 unsigned int chk_tree_depth;
96
97 /**
98 * In-memory cache of the current CHK tree.
99 * This struct will contain the CHK values
100 * from the root to the currently processed
101 * node in the tree as identified by
102 * "current_depth" and "publish_offset".
103 * The "chktree" will be initially NULL,
104 * then allocated to a sufficient number of
105 * entries for the size of the file and
106 * finally freed once the upload is complete.
107 */
108 struct ContentHashKey *chk_tree;
109
110 /**
111 * Are we currently in 'GNUNET_FS_tree_encoder_next'?
112 * Flag used to prevent recursion.
113 */
114 int in_next;
115};
116
117
118/**
119 * Compute the depth of the CHK tree.
120 *
121 * @param flen file length for which to compute the depth
122 * @return depth of the tree, always > 0. A depth of 1 means only a DBLOCK.
123 */
124unsigned int
125GNUNET_FS_compute_depth (uint64_t flen)
126{
127 unsigned int treeDepth;
128 uint64_t fl;
129
130 treeDepth = 1;
131 fl = DBLOCK_SIZE;
132 while (fl < flen)
133 {
134 treeDepth++;
135 if (fl * CHK_PER_INODE < fl)
136 {
137 /* integer overflow, this is a HUGE file... */
138 return treeDepth;
139 }
140 fl = fl * CHK_PER_INODE;
141 }
142 return treeDepth;
143}
144
145
146/**
147 * Calculate how many bytes of payload a block tree of the given
148 * depth MAY correspond to at most (this function ignores the fact that
149 * some blocks will only be present partially due to the total file
150 * size cutting some blocks off at the end).
151 *
152 * @param depth depth of the block. depth==0 is a DBLOCK.
153 * @return number of bytes of payload a subtree of this depth may correspond to
154 */
155uint64_t
156GNUNET_FS_tree_compute_tree_size (unsigned int depth)
157{
158 uint64_t rsize;
159 unsigned int i;
160
161 rsize = DBLOCK_SIZE;
162 for (i = 0; i < depth; i++)
163 rsize *= CHK_PER_INODE;
164 return rsize;
165}
166
167
168/**
169 * Compute the size of the current IBLOCK. The encoder is
170 * triggering the calculation of the size of an IBLOCK at the
171 * *end* (hence end_offset) of its construction. The IBLOCK
172 * maybe a full or a partial IBLOCK, and this function is to
173 * calculate how long it should be.
174 *
175 * @param depth depth of the IBlock in the tree, 0 would be a DBLOCK,
176 * must be > 0 (this function is for IBLOCKs only!)
177 * @param end_offset current offset in the payload (!) of the overall file,
178 * must be > 0 (since this function is called at the
179 * end of a block).
180 * @return size of the corresponding IBlock
181 */
182static uint16_t
183GNUNET_FS_tree_compute_iblock_size (unsigned int depth, uint64_t end_offset)
184{
185 unsigned int ret;
186 uint64_t mod;
187 uint64_t bds;
188
189 GNUNET_assert (depth > 0);
190 GNUNET_assert (end_offset > 0);
191 bds = GNUNET_FS_tree_compute_tree_size (depth);
192 mod = end_offset % bds;
193 if (0 == mod)
194 {
195 /* we were triggered at the end of a full block */
196 ret = CHK_PER_INODE;
197 }
198 else
199 {
200 /* we were triggered at the end of the file */
201 bds /= CHK_PER_INODE;
202 ret = mod / bds;
203 if (0 != mod % bds)
204 ret++;
205 }
206 return (uint16_t) (ret * sizeof(struct ContentHashKey));
207}
208
209
210size_t
211GNUNET_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 */
257struct GNUNET_FS_TreeEncoder *
258GNUNET_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 */
298static unsigned int
299compute_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 */
319void
320GNUNET_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 */
412struct GNUNET_FS_Uri *
413GNUNET_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 */
431void
432GNUNET_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 */
43unsigned int
44GNUNET_FS_compute_depth (uint64_t flen);
45
46
47/**
48 * Calculate how many bytes of payload a block tree of the given
49 * depth MAY correspond to at most (this function ignores the fact that
50 * some blocks will only be present partially due to the total file
51 * size cutting some blocks off at the end).
52 *
53 * @param depth depth of the block. depth==0 is a DBLOCK.
54 * @return number of bytes of payload a subtree of this depth may correspond to
55 */
56uint64_t
57GNUNET_FS_tree_compute_tree_size (unsigned int depth);
58
59
60/**
61 * Compute how many bytes of data should be stored in
62 * the specified block.
63 *
64 * @param fsize overall file size, must be > 0.
65 * @param offset offset in the original data corresponding
66 * to the beginning of the tree induced by the block;
67 * must be < fsize
68 * @param depth depth of the node in the tree, 0 for DBLOCK
69 * @return number of bytes stored in this node
70 */
71size_t
72GNUNET_FS_tree_calculate_block_size (uint64_t fsize, uint64_t offset,
73 unsigned int depth);
74
75
76/**
77 * Context for an ECRS-based file encoder that computes
78 * the Merkle-ish-CHK tree.
79 */
80struct GNUNET_FS_TreeEncoder;
81
82
83/**
84 * Function called asking for the current (encoded)
85 * block to be processed. After processing the
86 * client should either call "GNUNET_FS_tree_encode_next"
87 * or (on error) "GNUNET_FS_tree_encode_finish".
88 *
89 * @param cls closure
90 * @param chk content hash key for the block
91 * @param offset offset of the block
92 * @param depth depth of the block, 0 for DBLOCKs
93 * @param type type of the block (IBLOCK or DBLOCK)
94 * @param block the (encrypted) block
95 * @param block_size size of block (in bytes)
96 */
97typedef void (*GNUNET_FS_TreeBlockProcessor) (void *cls,
98 const struct ContentHashKey *chk,
99 uint64_t offset,
100 unsigned int depth,
101 enum GNUNET_BLOCK_Type type,
102 const void *block,
103 uint16_t block_size);
104
105
106/**
107 * Function called with information about our
108 * progress in computing the tree encoding.
109 *
110 * @param cls closure
111 * @param offset where are we in the file
112 * @param pt_block plaintext of the currently processed block
113 * @param pt_size size of pt_block
114 * @param depth depth of the block in the tree, 0 for DBLOCKS
115 */
116typedef void (*GNUNET_FS_TreeProgressCallback) (void *cls, uint64_t offset,
117 const void *pt_block,
118 size_t pt_size,
119 unsigned int depth);
120
121
122/**
123 * Initialize a tree encoder. This function will call "proc" and
124 * "progress" on each block in the tree. Once all blocks have been
125 * processed, "cont" will be scheduled. The "reader" will be called
126 * to obtain the (plaintext) blocks for the file. Note that this
127 * function will actually never call "proc"; the "proc" function must
128 * be triggered by calling "GNUNET_FS_tree_encoder_next" to trigger
129 * encryption (and calling of "proc") for each block.
130 *
131 * @param h the global FS context
132 * @param size overall size of the file to encode
133 * @param cls closure for reader, proc, progress and cont
134 * @param reader function to call to read plaintext data
135 * @param proc function to call on each encrypted block
136 * @param progress function to call with progress information
137 * @param cont function to call when done
138 * @return tree encoder context
139 */
140struct GNUNET_FS_TreeEncoder *
141GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h, uint64_t size,
142 void *cls, GNUNET_FS_DataReader reader,
143 GNUNET_FS_TreeBlockProcessor proc,
144 GNUNET_FS_TreeProgressCallback progress,
145 GNUNET_SCHEDULER_TaskCallback cont);
146
147
148/**
149 * Encrypt the next block of the file (and
150 * call proc and progress accordingly; or
151 * of course "cont" if we have already completed
152 * encoding of the entire file).
153 *
154 * @param te tree encoder to use
155 */
156void
157GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder *te);
158
159
160/**
161 * Get the resulting URI from the encoding.
162 *
163 * @param te the tree encoder to clean up
164 * @return uri set to the resulting URI (if encoding finished), NULL otherwise
165 */
166struct GNUNET_FS_Uri *
167GNUNET_FS_tree_encoder_get_uri (struct GNUNET_FS_TreeEncoder *te);
168
169
170/**
171 * Clean up a tree encoder and return information
172 * about possible errors.
173 *
174 * @param te the tree encoder to clean up
175 * @param emsg set to an error message (if an error occurred
176 * within the tree encoder; if this function is called
177 * prior to completion and prior to an internal error,
178 * both "*emsg" will be set to NULL).
179 */
180void
181GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder *te,
182 char **emsg);
183
184
185#if 0
186/* the functions below will be needed for persistence
187 but are not yet implemented -- FIXME... */
188/**
189 * Get data that would be needed to resume
190 * the encoding later.
191 *
192 * @param te encoding to resume
193 * @param data set to the resume data
194 * @param size set to the size of the resume data
195 */
196void
197GNUNET_FS_tree_encoder_resume_get_data (const struct GNUNET_FS_TreeEncoder *te,
198 void **data, size_t *size);
199
200
201/**
202 * Reset tree encoder to point previously
203 * obtained for resuming.
204 *
205 * @param te encoding to resume
206 * @param data the resume data
207 * @param size the size of the resume data
208 */
209void
210GNUNET_FS_tree_encoder_resume (struct GNUNET_FS_TreeEncoder *te,
211 const void *data, size_t size);
212
213#endif
214
215#endif
216
217/* end of fs_tree.h */
diff --git a/src/fs/fs_unindex.c b/src/fs/fs_unindex.c
deleted file mode 100644
index 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 */
51static size_t
52unindex_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 */
84void
85GNUNET_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 */
113static void
114unindex_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 */
138static void
139signal_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 */
159static void
160process_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 */
193static void
194unindex_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 */
234static void
235handle_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 */
264static void
265unindex_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 */
289static void
290unindex_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 */
354static void
355unindex_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 */
406void
407GNUNET_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 */
436static void
437continue_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 */
476static void
477process_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;
562get_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 */
580void
581GNUNET_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 */
633static void
634unindex_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 */
649void
650GNUNET_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 */
694void
695GNUNET_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 */
727void
728GNUNET_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 */
795struct GNUNET_FS_UnindexContext *
796GNUNET_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 */
837void
838GNUNET_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
95int
96GNUNET_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 */
147char *
148GNUNET_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 */
211static char *
212percent_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 */
271static struct GNUNET_FS_Uri *
272uri_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 */
365static struct GNUNET_FS_Uri *
366uri_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 */
405static struct GNUNET_FS_Uri *
406uri_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
452GNUNET_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 */
458struct 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};
480GNUNET_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 */
495static struct GNUNET_FS_Uri *
496uri_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 */
636struct GNUNET_FS_Uri *
637GNUNET_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 */
676void
677GNUNET_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 */
710unsigned int
711GNUNET_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
719int
720GNUNET_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 */
751void
752GNUNET_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 */
779void
780GNUNET_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 */
811int
812GNUNET_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 */
828struct GNUNET_TIME_Absolute
829GNUNET_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 */
842struct GNUNET_FS_Uri *
843GNUNET_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 */
868struct GNUNET_FS_Uri *
869GNUNET_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 */
907struct GNUNET_FS_Uri *
908GNUNET_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 */
930struct GNUNET_FS_Uri *
931GNUNET_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 */
986struct GNUNET_FS_Uri *
987GNUNET_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 */
1048struct GNUNET_FS_Uri *
1049GNUNET_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 */
1143struct GNUNET_FS_Uri *
1144GNUNET_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 */
1200int
1201GNUNET_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 */
1270int
1271GNUNET_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 */
1285int
1286GNUNET_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 */
1305char *
1306GNUNET_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 */
1323int
1324GNUNET_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 */
1345int
1346GNUNET_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 */
1359uint64_t
1360GNUNET_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 */
1383int
1384GNUNET_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 */
1399static void
1400insert_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 */
1420static int
1421find_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 */
1435static char *
1436normalize_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 */
1491static size_t
1492u8_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 */
1522static int
1523get_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 */
1630static int
1631get_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. '&lt;zlib&gt;' 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 */
1695static int
1696gather_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 */
1770struct GNUNET_FS_Uri *
1771GNUNET_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 */
1827static int
1828needs_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 */
1841static char *
1842uri_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 */
1911static char *
1912uri_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 */
1942static char *
1943uri_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 */
1973static char *
1974uri_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 */
2016char *
2017GNUNET_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 */
41struct WorkItem
42{
43 /**
44 * PENDING Work is kept in a linked list.
45 */
46 struct WorkItem *prev;
47
48 /**
49 * PENDING Work is kept in a linked list.
50 */
51 struct WorkItem *next;
52
53 /**
54 * Filename of the work item.
55 */
56 char *filename;
57
58 /**
59 * Unique identity for this work item (used to detect
60 * if we need to do the work again).
61 */
62 struct GNUNET_HashCode id;
63};
64
65
66/**
67 * Global return value from 'main'.
68 */
69static int ret;
70
71/**
72 * Are we running 'verbosely'?
73 */
74static unsigned int verbose;
75
76/**
77 * Configuration to use.
78 */
79static const struct GNUNET_CONFIGURATION_Handle *cfg;
80
81/**
82 * Name of the configuration file.
83 */
84static char *cfg_filename;
85
86/**
87 * Disable extractor option to use for publishing.
88 */
89static int disable_extractor;
90
91/**
92 * Disable creation time option to use for publishing.
93 */
94static int do_disable_creation_time;
95
96/**
97 * Handle for the main task that does scanning and working.
98 */
99static struct GNUNET_SCHEDULER_Task *run_task;
100
101/**
102 * Anonymity level option to use for publishing.
103 */
104static unsigned int anonymity_level = 1;
105
106/**
107 * Content priority option to use for publishing.
108 */
109static unsigned int content_priority = 365;
110
111/**
112 * Replication level option to use for publishing.
113 */
114static unsigned int replication_level = 1;
115
116/**
117 * Top-level directory we monitor to auto-publish.
118 */
119static const char *dir_name;
120
121/**
122 * Head of linked list of files still to publish.
123 */
124static struct WorkItem *work_head;
125
126/**
127 * Tail of linked list of files still to publish.
128 */
129static struct WorkItem *work_tail;
130
131/**
132 * Map from the hash of the filename (!) to a `struct WorkItem`
133 * that was finished.
134 */
135static struct GNUNET_CONTAINER_MultiHashMap *work_finished;
136
137/**
138 * Set to #GNUNET_YES if we are shutting down.
139 */
140static int do_shutdown;
141
142/**
143 * Start time of the current round; used to determine how long
144 * one iteration takes (which influences how fast we schedule
145 * the next one).
146 */
147static struct GNUNET_TIME_Absolute start_time;
148
149/**
150 * Pipe used to communicate 'gnunet-publish' completion (SIGCHLD) via signal.
151 */
152static struct GNUNET_DISK_PipeHandle *sigpipe;
153
154/**
155 * Handle to the 'gnunet-publish' process that we executed.
156 */
157static struct GNUNET_OS_Process *publish_proc;
158
159
160/**
161 * Compute the name of the state database file we will use.
162 */
163static char *
164get_state_file ()
165{
166 char *ret;
167
168 GNUNET_asprintf (&ret,
169 "%s%s.auto-share",
170 dir_name,
171 (DIR_SEPARATOR == dir_name[strlen (dir_name) - 1])
172 ? ""
173 : DIR_SEPARATOR_STR);
174 return ret;
175}
176
177
178/**
179 * Load the set of #work_finished items from disk.
180 */
181static void
182load_state ()
183{
184 char *fn;
185 struct GNUNET_BIO_ReadHandle *rh;
186 uint32_t n;
187 struct GNUNET_HashCode id;
188 struct WorkItem *wi;
189 char *emsg;
190
191 emsg = NULL;
192 fn = get_state_file ();
193 rh = GNUNET_BIO_read_open_file (fn);
194 GNUNET_free (fn);
195 if (NULL == rh)
196 return;
197 fn = NULL;
198 if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "number of files",
199 (int32_t *) &n))
200 goto error;
201 while (n-- > 0)
202 {
203 struct GNUNET_BIO_ReadSpec rs[] = {
204 GNUNET_BIO_read_spec_string ("filename", &fn, 1024),
205 GNUNET_BIO_read_spec_object ("id", &id, sizeof(struct GNUNET_HashCode)),
206 GNUNET_BIO_read_spec_end (),
207 };
208 if (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs))
209 goto error;
210 wi = GNUNET_new (struct WorkItem);
211 wi->id = id;
212 wi->filename = fn;
213 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
214 "Loaded serialization ID for `%s' is `%s'\n",
215 wi->filename,
216 GNUNET_h2s (&id));
217 fn = NULL;
218 GNUNET_CRYPTO_hash (wi->filename, strlen (wi->filename), &id);
219 GNUNET_break (GNUNET_OK ==
220 GNUNET_CONTAINER_multihashmap_put (
221 work_finished,
222 &id,
223 wi,
224 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
225 }
226 if (GNUNET_OK == GNUNET_BIO_read_close (rh, &emsg))
227 return;
228 rh = NULL;
229error:
230 GNUNET_free (fn);
231 if (NULL != rh)
232 (void) GNUNET_BIO_read_close (rh, &emsg);
233 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
234 _ ("Failed to load state: %s\n"),
235 emsg);
236 GNUNET_free (emsg);
237}
238
239
240/**
241 * Write work item from the #work_finished map to the given write handle.
242 *
243 * @param cls the `struct GNUNET_BIO_WriteHandle *`
244 * @param key key of the item in the map (unused)
245 * @param value the `struct WorkItem` to write
246 * @return #GNUNET_OK to continue to iterate (if write worked)
247 */
248static int
249write_item (void *cls, const struct GNUNET_HashCode *key, void *value)
250{
251 struct GNUNET_BIO_WriteHandle *wh = cls;
252 struct WorkItem *wi = value;
253
254 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
255 "Saving serialization ID of file `%s' with value `%s'\n",
256 wi->filename,
257 GNUNET_h2s (&wi->id));
258 struct GNUNET_BIO_WriteSpec ws[] = {
259 GNUNET_BIO_write_spec_string ("auto-share-write-item-filename",
260 wi->filename),
261 GNUNET_BIO_write_spec_object ("id", &wi->id, sizeof(struct
262 GNUNET_HashCode)),
263 GNUNET_BIO_write_spec_end (),
264 };
265 if (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))
266 return GNUNET_SYSERR; /* write error, abort iteration */
267 return GNUNET_OK;
268}
269
270
271/**
272 * Save the set of #work_finished items on disk.
273 */
274static void
275save_state ()
276{
277 uint32_t n;
278 struct GNUNET_BIO_WriteHandle *wh;
279 char *fn;
280
281 n = GNUNET_CONTAINER_multihashmap_size (work_finished);
282 fn = get_state_file ();
283 wh = GNUNET_BIO_write_open_file (fn);
284 if (NULL == wh)
285 {
286 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
287 _ ("Failed to save state to file %s\n"),
288 fn);
289 GNUNET_free (fn);
290 return;
291 }
292 if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "size of state", n))
293 {
294 (void) GNUNET_BIO_write_close (wh, NULL);
295 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
296 _ ("Failed to save state to file %s\n"),
297 fn);
298 GNUNET_free (fn);
299 return;
300 }
301 (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished, &write_item, wh);
302 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
303 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
304 _ ("Failed to save state to file %s\n"),
305 fn);
306 GNUNET_free (fn);
307}
308
309
310/**
311 * Task run on shutdown. Serializes our current state to disk.
312 *
313 * @param cls closure, unused
314 */
315static void
316do_stop_task (void *cls)
317{
318 do_shutdown = GNUNET_YES;
319 if (NULL != publish_proc)
320 {
321 GNUNET_OS_process_kill (publish_proc, SIGKILL);
322 return;
323 }
324 if (NULL != run_task)
325 {
326 GNUNET_SCHEDULER_cancel (run_task);
327 run_task = NULL;
328 }
329}
330
331
332/**
333 * Decide what the next task is (working or scanning) and schedule it.
334 */
335static void
336schedule_next_task (void);
337
338
339/**
340 * Task triggered whenever we receive a SIGCHLD (child
341 * process died).
342 *
343 * @param cls the `struct WorkItem` we were working on
344 */
345static void
346maint_child_death (void *cls)
347{
348 struct WorkItem *wi = cls;
349 struct GNUNET_HashCode key;
350 enum GNUNET_OS_ProcessStatusType type;
351 unsigned long code;
352 int ret;
353 char c;
354 const struct GNUNET_DISK_FileHandle *pr;
355 const struct GNUNET_SCHEDULER_TaskContext *tc;
356
357 run_task = NULL;
358 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
359 tc = GNUNET_SCHEDULER_get_task_context ();
360 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
361 {
362 /* shutdown scheduled us, someone else will kill child,
363 we should just try again */
364 run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
365 pr,
366 &maint_child_death,
367 wi);
368 return;
369 }
370 /* consume the signal */
371 GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof(c)));
372
373 ret = GNUNET_OS_process_status (publish_proc, &type, &code);
374 GNUNET_assert (GNUNET_SYSERR != ret);
375 if (GNUNET_NO == ret)
376 {
377 /* process still running? Then where did the SIGCHLD come from?
378 Well, let's declare it spurious (kernel bug?) and keep rolling.
379 */
380 GNUNET_break (0);
381 run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
382 pr,
383 &maint_child_death,
384 wi);
385 return;
386 }
387 GNUNET_assert (GNUNET_OK == ret);
388
389 GNUNET_OS_process_destroy (publish_proc);
390 publish_proc = NULL;
391
392 if (GNUNET_YES == do_shutdown)
393 {
394 GNUNET_free (wi->filename);
395 GNUNET_free (wi);
396 return;
397 }
398 if ((GNUNET_OS_PROCESS_EXITED == type) && (0 == code))
399 {
400 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
401 _ ("Publication of `%s' done\n"),
402 wi->filename);
403 GNUNET_CRYPTO_hash (wi->filename, strlen (wi->filename), &key);
404 GNUNET_break (GNUNET_OK ==
405 GNUNET_CONTAINER_multihashmap_put (
406 work_finished,
407 &key,
408 wi,
409 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
410 }
411 else
412 {
413 GNUNET_CONTAINER_DLL_insert_tail (work_head, work_tail, wi);
414 }
415 save_state ();
416 schedule_next_task ();
417}
418
419
420/**
421 * Signal handler called for SIGCHLD. Triggers the
422 * respective handler by writing to the trigger pipe.
423 */
424static void
425sighandler_child_death ()
426{
427 static char c;
428 int old_errno = errno; /* back-up errno */
429
430 GNUNET_break (
431 1 ==
432 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe,
433 GNUNET_DISK_PIPE_END_WRITE),
434 &c,
435 sizeof(c)));
436 errno = old_errno; /* restore errno */
437}
438
439
440/**
441 * Function called to process work items.
442 *
443 * @param cls closure, NULL
444 */
445static void
446work (void *cls)
447{
448 static char *argv[14];
449 static char anon_level[20];
450 static char content_prio[20];
451 static char repl_level[20];
452 struct WorkItem *wi;
453 const struct GNUNET_DISK_FileHandle *pr;
454 int argc;
455
456 run_task = NULL;
457 wi = work_head;
458 GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi);
459 argc = 0;
460 argv[argc++] = "gnunet-publish";
461 if (verbose)
462 argv[argc++] = "-V";
463 if (disable_extractor)
464 argv[argc++] = "-D";
465 if (do_disable_creation_time)
466 argv[argc++] = "-d";
467 argv[argc++] = "-c";
468 argv[argc++] = cfg_filename;
469 GNUNET_snprintf (anon_level, sizeof(anon_level), "%u", anonymity_level);
470 argv[argc++] = "-a";
471 argv[argc++] = anon_level;
472 GNUNET_snprintf (content_prio, sizeof(content_prio), "%u", content_priority);
473 argv[argc++] = "-p";
474 argv[argc++] = content_prio;
475 GNUNET_snprintf (repl_level, sizeof(repl_level), "%u", replication_level);
476 argv[argc++] = "-r";
477 argv[argc++] = repl_level;
478 argv[argc++] = wi->filename;
479 argv[argc] = NULL;
480 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Publishing `%s'\n"), wi->filename);
481 GNUNET_assert (NULL == publish_proc);
482 publish_proc = GNUNET_OS_start_process_vap (GNUNET_OS_USE_PIPE_CONTROL,
483 NULL,
484 NULL,
485 NULL,
486 "gnunet-publish",
487 argv);
488 if (NULL == publish_proc)
489 {
490 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
491 _ ("Failed to run `%s'\n"),
492 "gnunet-publish");
493 GNUNET_CONTAINER_DLL_insert (work_head, work_tail, wi);
494 run_task =
495 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, &work, NULL);
496 return;
497 }
498 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
499 run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
500 pr,
501 &maint_child_death,
502 wi);
503}
504
505
506/**
507 * Recursively scan the given file/directory structure to determine
508 * a unique ID that represents the current state of the hierarchy.
509 *
510 * @param cls where to store the unique ID we are computing
511 * @param filename file to scan
512 * @return #GNUNET_OK (always)
513 */
514static int
515determine_id (void *cls, const char *filename)
516{
517 struct GNUNET_HashCode *id = cls;
518 struct stat sbuf;
519 struct GNUNET_HashCode fx[2];
520 struct GNUNET_HashCode ft;
521
522 if (0 != stat (filename, &sbuf))
523 {
524 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
525 return GNUNET_OK;
526 }
527 GNUNET_CRYPTO_hash (filename, strlen (filename), &fx[0]);
528 if (! S_ISDIR (sbuf.st_mode))
529 {
530 uint64_t fattr[2];
531
532 fattr[0] = GNUNET_htonll (sbuf.st_size);
533 fattr[0] = GNUNET_htonll (sbuf.st_mtime);
534
535 GNUNET_CRYPTO_hash (fattr, sizeof(fattr), &fx[1]);
536 }
537 else
538 {
539 memset (&fx[1], 1, sizeof(struct GNUNET_HashCode));
540 GNUNET_DISK_directory_scan (filename, &determine_id, &fx[1]);
541 }
542 /* use hash here to make hierarchical structure distinct from
543 all files on the same level */
544 GNUNET_CRYPTO_hash (fx, sizeof(fx), &ft);
545 /* use XOR here so that order of the files in the directory
546 does not matter! */
547 GNUNET_CRYPTO_hash_xor (&ft, id, id);
548 return GNUNET_OK;
549}
550
551
552/**
553 * Function called with a filename (or directory name) to publish
554 * (if it has changed since the last time we published it). This function
555 * is called for the top-level files only.
556 *
557 * @param cls closure, NULL
558 * @param filename complete filename (absolute path)
559 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR during shutdown
560 */
561static int
562add_file (void *cls, const char *filename)
563{
564 struct WorkItem *wi;
565 struct GNUNET_HashCode key;
566 struct GNUNET_HashCode id;
567
568 if (GNUNET_YES == do_shutdown)
569 return GNUNET_SYSERR;
570 if ((NULL != strstr (filename, "/.auto-share")) ||
571 (NULL != strstr (filename, "\\.auto-share")))
572 return GNUNET_OK; /* skip internal file */
573 GNUNET_CRYPTO_hash (filename, strlen (filename), &key);
574 wi = GNUNET_CONTAINER_multihashmap_get (work_finished, &key);
575 memset (&id, 0, sizeof(struct GNUNET_HashCode));
576 determine_id (&id, filename);
577 if (NULL != wi)
578 {
579 if (0 == memcmp (&id, &wi->id, sizeof(struct GNUNET_HashCode)))
580 return GNUNET_OK; /* skip: we did this one already */
581 /* contents changed, need to re-do the directory... */
582 GNUNET_assert (
583 GNUNET_YES ==
584 GNUNET_CONTAINER_multihashmap_remove (work_finished, &key, wi));
585 }
586 else
587 {
588 wi = GNUNET_new (struct WorkItem);
589 wi->filename = GNUNET_strdup (filename);
590 }
591 wi->id = id;
592 GNUNET_CONTAINER_DLL_insert (work_head, work_tail, wi);
593 if (GNUNET_YES == do_shutdown)
594 return GNUNET_SYSERR;
595 return GNUNET_OK;
596}
597
598
599/**
600 * Periodically run task to update our view of the directory to share.
601 *
602 * @param cls NULL
603 */
604static void
605scan (void *cls)
606{
607 run_task = NULL;
608 start_time = GNUNET_TIME_absolute_get ();
609 (void) GNUNET_DISK_directory_scan (dir_name, &add_file, NULL);
610 schedule_next_task ();
611}
612
613
614/**
615 * Decide what the next task is (working or scanning) and schedule it.
616 */
617static void
618schedule_next_task ()
619{
620 struct GNUNET_TIME_Relative delay;
621
622 if (GNUNET_YES == do_shutdown)
623 return;
624 GNUNET_assert (NULL == run_task);
625 if (NULL == work_head)
626 {
627 /* delay by at most 4h, at least 1s, and otherwise in between depending
628 on how long it took to scan */
629 delay = GNUNET_TIME_absolute_get_duration (start_time);
630 delay = GNUNET_TIME_relative_saturating_multiply (delay, 100);
631 delay = GNUNET_TIME_relative_min (delay, MAX_DELAY);
632 delay = GNUNET_TIME_relative_max (delay, MIN_DELAY);
633 run_task = GNUNET_SCHEDULER_add_delayed (delay, &scan, NULL);
634 }
635 else
636 {
637 run_task = GNUNET_SCHEDULER_add_now (&work, NULL);
638 }
639}
640
641
642/**
643 * Main function that will be run by the scheduler.
644 *
645 * @param cls closure
646 * @param args remaining command-line arguments
647 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
648 * @param c configuration
649 */
650static void
651run (void *cls,
652 char *const *args,
653 const char *cfgfile,
654 const struct GNUNET_CONFIGURATION_Handle *c)
655{
656 /* check arguments */
657 if ((NULL == args[0]) || (NULL != args[1]) ||
658 (GNUNET_YES != GNUNET_DISK_directory_test (args[0], GNUNET_YES)))
659 {
660 printf (_ (
661 "You must specify one and only one directory name for automatic publication.\n"));
662 ret = -1;
663 return;
664 }
665 cfg_filename = GNUNET_strdup (cfgfile);
666 cfg = c;
667 dir_name = args[0];
668 work_finished = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO);
669 load_state ();
670 run_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
671 &scan,
672 NULL);
673 GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL);
674}
675
676
677/**
678 * Free memory associated with the work item from the work_finished map.
679 *
680 * @param cls NULL (unused)
681 * @param key key of the item in the map (unused)
682 * @param value the `struct WorkItem` to free
683 * @return #GNUNET_OK to continue to iterate
684 */
685static int
686free_item (void *cls, const struct GNUNET_HashCode *key, void *value)
687{
688 struct WorkItem *wi = value;
689
690 GNUNET_free (wi->filename);
691 GNUNET_free (wi);
692 return GNUNET_OK;
693}
694
695
696/**
697 * The main function to automatically publish content to GNUnet.
698 *
699 * @param argc number of arguments from the command line
700 * @param argv command line arguments
701 * @return 0 ok, 1 on error
702 */
703int
704main (int argc, char *const *argv)
705{
706 struct GNUNET_GETOPT_CommandLineOption options[] = {
707 GNUNET_GETOPT_option_uint ('a',
708 "anonymity",
709 "LEVEL",
710 gettext_noop (
711 "set the desired LEVEL of sender-anonymity"),
712 &anonymity_level),
713
714 GNUNET_GETOPT_option_flag (
715 'd',
716 "disable-creation-time",
717 gettext_noop (
718 "disable adding the creation time to the metadata of the uploaded file"),
719 &do_disable_creation_time),
720
721 GNUNET_GETOPT_option_flag (
722 'D',
723 "disable-extractor",
724 gettext_noop ("do not use libextractor to add keywords or metadata"),
725 &disable_extractor),
726
727 GNUNET_GETOPT_option_uint ('p',
728 "priority",
729 "PRIORITY",
730 gettext_noop (
731 "specify the priority of the content"),
732 &content_priority),
733
734 GNUNET_GETOPT_option_uint ('r',
735 "replication",
736 "LEVEL",
737 gettext_noop (
738 "set the desired replication LEVEL"),
739 &replication_level),
740
741 GNUNET_GETOPT_option_verbose (&verbose),
742
743 GNUNET_GETOPT_OPTION_END
744 };
745 struct WorkItem *wi;
746 int ok;
747 struct GNUNET_SIGNAL_Context *shc_chld;
748
749 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
750 return 2;
751 sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
752 GNUNET_assert (NULL != sigpipe);
753 shc_chld =
754 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
755 ok =
756 (GNUNET_OK ==
757 GNUNET_PROGRAM_run (
758 argc,
759 argv,
760 "gnunet-auto-share [OPTIONS] FILENAME",
761 gettext_noop ("Automatically publish files from a directory on GNUnet"),
762 options,
763 &run,
764 NULL))
765 ? ret
766 : 1;
767 if (NULL != work_finished)
768 {
769 (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished,
770 &free_item,
771 NULL);
772 GNUNET_CONTAINER_multihashmap_destroy (work_finished);
773 }
774 while (NULL != (wi = work_head))
775 {
776 GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi);
777 GNUNET_free (wi->filename);
778 GNUNET_free (wi);
779 }
780 GNUNET_SIGNAL_handler_uninstall (shc_chld);
781 shc_chld = NULL;
782 GNUNET_DISK_pipe_close (sigpipe);
783 sigpipe = NULL;
784 GNUNET_free (cfg_filename);
785 cfg_filename = NULL;
786 GNUNET_free_nz ((void *) argv);
787 return ok;
788}
789
790
791/* end of gnunet-auto-share.c */
diff --git a/src/fs/gnunet-daemon-fsprofiler.c b/src/fs/gnunet-daemon-fsprofiler.c
deleted file mode 100644
index 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 */
38struct 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 */
95static int global_ret;
96
97/**
98 * Configuration we use.
99 */
100static const struct GNUNET_CONFIGURATION_Handle *cfg;
101
102/**
103 * Handle to the statistics service.
104 */
105static struct GNUNET_STATISTICS_Handle *stats_handle;
106
107/**
108 * Peer's FS handle.
109 */
110static struct GNUNET_FS_Handle *fs_handle;
111
112/**
113 * Unique number for this peer in the testbed.
114 */
115static unsigned long long my_peerid;
116
117/**
118 * Desired anonymity level.
119 */
120static unsigned long long anonymity_level;
121
122/**
123 * Desired replication level.
124 */
125static 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 */
134static char *publish_pattern;
135
136/**
137 * Head of the DLL of publish patterns.
138 */
139static struct Pattern *publish_head;
140
141/**
142 * Tail of the DLL of publish patterns.
143 */
144static 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 */
154static char *download_pattern;
155
156/**
157 * Head of the DLL of publish patterns.
158 */
159static struct Pattern *download_head;
160
161/**
162 * Tail of the DLL of publish patterns.
163 */
164static 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 */
176static int
177parse_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 */
209static struct GNUNET_FS_Uri *
210make_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 */
229static struct GNUNET_FS_FileInformation *
230make_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 */
272static void
273shutdown_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 */
317static void
318publish_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 */
332static void
333download_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 */
347static void
348search_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 */
372static void *
373progress_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 */
516static void
517start_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 */
537static void
538start_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 */
561static void
562run (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 */
655int
656main (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
29static 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 */
43static int
44item_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 */
90static void
91print_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 */
130static void
131run (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 */
186int
187main (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
32static int ret;
33
34static unsigned int verbose;
35
36static int delete_incomplete;
37
38static const struct GNUNET_CONFIGURATION_Handle *cfg;
39
40static struct GNUNET_FS_Handle *ctx;
41
42static struct GNUNET_FS_DownloadContext *dc;
43
44static unsigned int anonymity = 1;
45
46static unsigned int parallelism = 16;
47
48static unsigned int request_parallelism = 4092;
49
50static int do_recursive;
51
52static char *filename;
53
54static int local_only;
55
56
57static void
58cleanup_task (void *cls)
59{
60 GNUNET_FS_stop (ctx);
61 ctx = NULL;
62}
63
64
65static void
66shutdown_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 */
83static void
84display_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 */
119static void *
120progress_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 */
222static void
223run (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 */
311int
312main (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 &parallelism),
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 */
33static int ret;
34
35/**
36 * Data file with the hosts for the testbed.
37 */
38static char *host_filename;
39
40/**
41 * Number of peers to run in the experiment.
42 */
43static unsigned int num_peers;
44
45/**
46 * After how long do we abort the test?
47 */
48static struct GNUNET_TIME_Relative timeout;
49
50/**
51 * Handle to the task run during termination.
52 */
53static struct GNUNET_SCHEDULER_Task *terminate_taskid;
54
55
56/**
57 * Function called after we've collected the statistics.
58 *
59 * @param cls NULL
60 * @param op the operation that has been finished
61 * @param emsg error message in case the operation has failed; will be NULL if
62 * operation has executed successfully.
63 */
64static void
65shutdown_task (void *cls,
66 struct GNUNET_TESTBED_Operation *op,
67 const char *emsg)
68{
69 if (NULL != emsg)
70 fprintf (stderr,
71 "Error collecting statistics: %s\n",
72 emsg);
73 GNUNET_SCHEDULER_shutdown ();
74}
75
76
77/**
78 * Callback function to process statistic values from all peers.
79 * Prints them out.
80 *
81 * @param cls closure
82 * @param peer the peer the statistic belong to
83 * @param subsystem name of subsystem that created the statistic
84 * @param name the name of the datum
85 * @param value the current value
86 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
87 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
88 */
89static int
90process_stats (void *cls,
91 const struct GNUNET_TESTBED_Peer *peer,
92 const char *subsystem,
93 const char *name,
94 uint64_t value,
95 int is_persistent)
96{
97 fprintf (stdout,
98 "%p-%s: %s = %llu\n",
99 peer,
100 subsystem,
101 name,
102 (unsigned long long) value);
103 return GNUNET_OK;
104}
105
106
107/**
108 * Task run on shutdown to terminate. Triggers printing out
109 * all statistics.
110 *
111 * @param cls NULL
112 */
113static void
114terminate_task (void *cls)
115{
116 if (NULL != terminate_taskid)
117 {
118 GNUNET_SCHEDULER_cancel (terminate_taskid);
119 terminate_taskid = NULL;
120 }
121 GNUNET_TESTBED_get_statistics (0, NULL,
122 NULL, NULL,
123 &process_stats,
124 &shutdown_task,
125 NULL);
126}
127
128
129/**
130 * Task run on timeout to terminate. Triggers printing out
131 * all statistics.
132 *
133 * @param cls NULL
134 */
135static void
136timeout_task (void *cls)
137{
138 terminate_taskid = NULL;
139 GNUNET_SCHEDULER_shutdown ();
140}
141
142
143/**
144 * Signature of a main function for a testcase.
145 *
146 * @param cls closure
147 * @param h the run handle
148 * @param num_peers number of peers in 'peers'
149 * @param peers handle to peers run in the testbed
150 * @param links_succeeded the number of overlay link connection attempts that
151 * succeeded
152 * @param links_failed the number of overlay link connection attempts that
153 * failed
154 */
155static void
156test_master (void *cls,
157 struct GNUNET_TESTBED_RunHandle *h,
158 unsigned int num_peers,
159 struct GNUNET_TESTBED_Peer **peers,
160 unsigned int links_succeeded,
161 unsigned int links_failed)
162{
163 // const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
164 // FIXME: enable clients to signal 'completion' before timeout;
165 // in that case, run the 'terminate_task' "immediately"
166
167 if (0 != timeout.rel_value_us)
168 terminate_taskid = GNUNET_SCHEDULER_add_delayed (timeout,
169 &timeout_task,
170 NULL);
171 GNUNET_SCHEDULER_add_shutdown (&terminate_task,
172 NULL);
173}
174
175
176/**
177 * Main function that will be run by the scheduler.
178 *
179 * @param cls closure
180 * @param args remaining command-line arguments
181 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
182 * @param cfg configuration
183 */
184static void
185run (void *cls, char *const *args, const char *cfgfile,
186 const struct GNUNET_CONFIGURATION_Handle *cfg)
187{
188 GNUNET_TESTBED_run (host_filename,
189 cfg,
190 num_peers,
191 0, NULL, NULL,
192 &test_master, (void *) cfg);
193}
194
195
196/**
197 * Program to run a file-sharing testbed.
198 *
199 * @param argc number of arguments from the command line
200 * @param argv command line arguments
201 * @return 0 ok, 1 on error
202 */
203int
204main (int argc, char *const *argv)
205{
206 struct GNUNET_GETOPT_CommandLineOption options[] = {
207 GNUNET_GETOPT_option_uint ('n',
208 "num-peers",
209 "COUNT",
210 gettext_noop (
211 "run the experiment with COUNT peers"),
212 &num_peers),
213
214 GNUNET_GETOPT_option_string ('H',
215 "hosts",
216 "HOSTFILE",
217 gettext_noop (
218 "specifies name of a file with the HOSTS the testbed should use"),
219 &host_filename),
220
221 GNUNET_GETOPT_option_relative_time ('t',
222 "timeout",
223 "DELAY",
224 gettext_noop (
225 "automatically terminate experiment after DELAY"),
226 &timeout),
227
228 GNUNET_GETOPT_OPTION_END
229 };
230
231 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
232 return 2;
233
234 ret = (GNUNET_OK ==
235 GNUNET_PROGRAM_run (argc, argv, "gnunet-fs-profiler",
236 gettext_noop (
237 "run a testbed to measure file-sharing performance"),
238 options, &run,
239 NULL)) ? ret : 1;
240 GNUNET_free_nz ((void *) argv);
241 return ret;
242}
243
244
245/* end of gnunet-fs-profiler.c */
diff --git a/src/fs/gnunet-fs.c b/src/fs/gnunet-fs.c
deleted file mode 100644
index 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 */
32static int ret;
33
34/**
35 * Handle to FS service.
36 */
37static struct GNUNET_FS_Handle *fs;
38
39/**
40 * Handle for the index listing operation.
41 */
42static struct GNUNET_FS_GetIndexedContext *gic;
43
44/**
45 * Option -i given?
46 */
47static int list_indexed_files;
48
49/**
50 * Option -v given?
51 */
52static 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 */
63static enum GNUNET_GenericReturnValue
64print_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 */
92static void
93do_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 */
117static void
118run (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 */
157int
158main (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 */
37struct 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 */
88static struct EXTRACTOR_PluginList *plugins;
89#endif
90
91/**
92 * File descriptor we use for IPC with the parent.
93 */
94static 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. '&lt;zlib&gt;' 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 */
115static int
116add_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 */
163static void
164free_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 */
186static int
187write_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 */
217static int
218write_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 */
247static int
248preprocess_file (const char *filename, struct ScanTreeNode **dst);
249
250
251/**
252 * Closure for the 'scan_callback'
253 */
254struct 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 */
277static int
278scan_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 */
308static int
309preprocess_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 */
374static int
375extract_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 */
445static void
446ignore_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 */
472static void
473make_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 */
497int
498main (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 */
36static int ret;
37
38/**
39 * Command line option 'verbose' set
40 */
41static unsigned int verbose;
42
43/**
44 * Handle to our configuration.
45 */
46static const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48/**
49 * Handle for interaction with file-sharing service.
50 */
51static struct GNUNET_FS_Handle *ctx;
52
53/**
54 * Handle to FS-publishing operation.
55 */
56static struct GNUNET_FS_PublishContext *pc;
57
58/**
59 * Meta-data provided via command-line option.
60 */
61static struct GNUNET_FS_MetaData *meta;
62
63/**
64 * Keywords provided via command-line option.
65 */
66static struct GNUNET_FS_Uri *topKeywords;
67
68/**
69 * Options we set for published blocks.
70 */
71static 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 */
77static 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 */
84static struct GNUNET_FS_Uri *uri;
85
86/**
87 * Command-line option for namespace publishing: identifier for updates
88 * to this publication.
89 */
90static char *next_id;
91
92/**
93 * Command-line option for namespace publishing: identifier for this
94 * publication.
95 */
96static char *this_id;
97
98/**
99 * Command-line option identifying the pseudonym to use for the publication.
100 */
101static char *pseudonym;
102
103/**
104 * Command-line option for 'inserting'
105 */
106static int do_insert;
107
108/**
109 * Command-line option to disable meta data extraction.
110 */
111static int disable_extractor;
112
113/**
114 * Command-line option to merely simulate publishing operation.
115 */
116static int do_simulate;
117
118/**
119 * Command-line option to only perform meta data extraction, but not publish.
120 */
121static int extract_only;
122
123/**
124 * Command-line option to disable adding creation time.
125 */
126static int enable_creation_time;
127
128/**
129 * Handle to the directory scanner (for recursive insertions).
130 */
131static 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 */
137static struct GNUNET_IDENTITY_Ego *namespace;
138
139/**
140 * Handle to identity service.
141 */
142static 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 */
151static void
152do_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 */
203static void *
204progress_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 */
320static int
321meta_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 */
351static int
352keyword_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 */
376static int
377publish_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 */
450static void
451uri_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 */
473static void
474uri_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 */
515static struct GNUNET_FS_FileInformation *
516get_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 */
572static void
573directory_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 */
631static void
632directory_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 */
698static void
699identity_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 */
774static void
775identity_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 */
802static void
803run (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 */
889int
890main (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
65enum 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
73struct GNUNET_SEARCH_MetadataPrinterInfo
74{
75 unsigned int counter;
76 unsigned int flags;
77 int type;
78};
79
80
81static int ret;
82
83static const struct GNUNET_CONFIGURATION_Handle *cfg;
84
85static struct GNUNET_FS_Handle *ctx;
86
87static struct GNUNET_FS_SearchContext *sc;
88
89static char *output_filename;
90
91static char *format_string;
92
93static char *dir_format_string;
94
95static char *meta_format_string;
96
97static struct GNUNET_FS_DirectoryBuilder *db;
98
99static unsigned int anonymity = 1;
100
101/**
102 * Timeout for the search, 0 means to wait for CTRL-C.
103 */
104static struct GNUNET_TIME_Relative timeout;
105
106static unsigned int results_limit;
107
108static unsigned int results;
109
110static unsigned int verbose;
111
112static int bookmark_only;
113
114static int local_only;
115
116static int silent_mode;
117
118static struct GNUNET_SCHEDULER_Task *tt;
119
120static 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 */
134static const char *
135print_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. '&lt;zlib&gt;' 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 */
199static int
200item_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 */
282static void
283print_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
420static void
421clean_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 */
468static void *
469progress_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
554static void
555shutdown_task (void *const cls)
556{
557 if (sc != NULL)
558 {
559 GNUNET_FS_search_stop (sc);
560 sc = NULL;
561 }
562}
563
564
565static void
566timeout_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 */
582static void
583run (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 */
694int
695main (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 */
73struct 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 */
106struct 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 */
134struct 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 */
171struct 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 */
222struct GNUNET_DATASTORE_Handle *GSF_dsh;
223
224/**
225 * Our configuration.
226 */
227const struct GNUNET_CONFIGURATION_Handle *GSF_cfg;
228
229/**
230 * Handle for reporting statistics.
231 */
232struct GNUNET_STATISTICS_Handle *GSF_stats;
233
234/**
235 * Handle for DHT operations.
236 */
237struct GNUNET_DHT_Handle *GSF_dht;
238
239/**
240 * How long do requests typically stay in the routing table?
241 */
242struct 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 */
248struct 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 */
261double GSF_current_priorities;
262
263/**
264 * Size of the datastore queue we assume for common requests.
265 */
266unsigned 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 */
272unsigned 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 */
278unsigned int GSF_cover_content_count;
279
280/**
281 * Our block context.
282 */
283struct 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 */
289struct GNUNET_CORE_Handle *GSF_core;
290
291/**
292 * Are we introducing randomized delays for better anonymity?
293 */
294int GSF_enable_randomized_delays;
295
296/**
297 * Identity of this peer.
298 */
299struct GNUNET_PeerIdentity GSF_my_id;
300
301/* ***************************** locals ******************************* */
302
303/**
304 * Configuration for block library.
305 */
306static struct GNUNET_CONFIGURATION_Handle *block_cfg;
307
308/**
309 * Private key of this peer. Used to sign LOC URI requests.
310 */
311static struct GNUNET_CRYPTO_EddsaPrivateKey pk;
312
313/**
314 * ID of our task that we use to age the cover counters.
315 */
316static struct GNUNET_SCHEDULER_Task *cover_age_task;
317
318/**
319 * Datastore 'GET' load tracking.
320 */
321static 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 */
332static void *
333client_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 */
351static void
352client_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 */
389static void
390client_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 */
464static void
465client_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 */
504static void
505age_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 */
522void
523GSF_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 */
542int
543GSF_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 */
564static int
565check_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 */
589static void
590consider_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 */
622void
623GSF_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 */
645static int
646check_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 */
691static void
692start_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 */
745static int
746check_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 */
771static void
772handle_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 */
880static void
881handle_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 */
920static int
921check_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 */
949static void
950signal_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 */
975static void
976hash_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 */
1022static void
1023handle_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 */
1086static void
1087handle_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 */
1103static void
1104handle_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 */
1131static void
1132shutdown_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 */
1179static void
1180peer_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 */
1198static int
1199main_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 */
1294static void
1295run (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 */
1348GNUNET_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
76GNUNET_NETWORK_STRUCT_BEGIN
77
78/**
79 * Message sent between peers asking for FS-content.
80 */
81struct 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 */
133struct 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};
151GNUNET_NETWORK_STRUCT_END
152
153/**
154 * A connected peer.
155 */
156struct GSF_ConnectedPeer;
157
158/**
159 * An active request.
160 */
161struct GSF_PendingRequest;
162
163/**
164 * A local client.
165 */
166struct GSF_LocalClient;
167
168/**
169 * Information kept per plan per request ('pe' module).
170 */
171struct GSF_RequestPlan;
172
173/**
174 * Bijection between request plans and pending requests.
175 */
176struct GSF_PendingRequestPlanBijection;
177
178/**
179 * Our connection to the datastore.
180 */
181extern struct GNUNET_DATASTORE_Handle *GSF_dsh;
182
183/**
184 * Our configuration.
185 */
186extern const struct GNUNET_CONFIGURATION_Handle *GSF_cfg;
187
188/**
189 * Handle for reporting statistics.
190 */
191extern 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 */
197extern struct GNUNET_CORE_Handle *GSF_core;
198
199/**
200 * Handle for DHT operations.
201 */
202extern struct GNUNET_DHT_Handle *GSF_dht;
203
204/**
205 * How long do requests typically stay in the routing table?
206 */
207extern struct GNUNET_LOAD_Value *GSF_rt_entry_lifetime;
208
209/**
210 * Running average of the observed latency to other peers (round trip).
211 */
212extern struct GNUNET_TIME_Relative GSF_avg_latency;
213
214/**
215 * Handle to ATS service.
216 */
217extern struct GNUNET_ATS_PerformanceHandle *GSF_ats;
218
219/**
220 * Identity of this peer.
221 */
222extern 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 */
234extern 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 */
240extern 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 */
246extern unsigned int GSF_cover_content_count;
247
248/**
249 * Our block context.
250 */
251extern struct GNUNET_BLOCK_Context *GSF_block_ctx;
252
253/**
254 * Are we introducing randomized delays for better anonymity?
255 */
256extern int GSF_enable_randomized_delays;
257
258/**
259 * Size of the datastore queue we assume for common requests.
260 */
261extern 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 */
274void
275GSF_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 */
289int
290GSF_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 */
299void
300GSF_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 */
32struct GSF_CadetRequest;
33
34
35/**
36 * Function called with a reply from the cadet.
37 *
38 * @param cls closure
39 * @param type type of the block, ANY on error
40 * @param expiration expiration time for the block
41 * @param data_size number of bytes in @a data, 0 on error
42 * @param data reply block data, NULL on error
43 */
44typedef void
45(*GSF_CadetReplyProcessor)(void *cls,
46 enum GNUNET_BLOCK_Type type,
47 struct GNUNET_TIME_Absolute expiration,
48 size_t data_size,
49 const void *data);
50
51
52/**
53 * Look for a block by directly contacting a particular peer.
54 *
55 * @param target peer that should have the block
56 * @param query hash to query for the block
57 * @param type desired type for the block
58 * @param proc function to call with result
59 * @param proc_cls closure for @a proc
60 * @return handle to cancel the operation
61 */
62struct GSF_CadetRequest *
63GSF_cadet_query (const struct GNUNET_PeerIdentity *target,
64 const struct GNUNET_HashCode *query,
65 enum GNUNET_BLOCK_Type type,
66 GSF_CadetReplyProcessor proc,
67 void *proc_cls);
68
69/**
70 * Function called on each active cadets to shut them down.
71 *
72 * @param cls NULL
73 * @param key target peer, unused
74 * @param value the `struct CadetHandle` to destroy
75 * @return #GNUNET_YES (continue to iterate)
76 */
77int
78GSF_cadet_release_clients (void *cls,
79 const struct GNUNET_PeerIdentity *key,
80 void *value);
81
82
83/**
84 * Cancel an active request; must not be called after 'proc'
85 * was called.
86 *
87 * @param sr request to cancel
88 */
89void
90GSF_cadet_query_cancel (struct GSF_CadetRequest *sr);
91
92
93/**
94 * Initialize subsystem for non-anonymous file-sharing.
95 */
96void
97GSF_cadet_start_server (void);
98
99
100/**
101 * Shutdown subsystem for non-anonymous file-sharing.
102 */
103void
104GSF_cadet_stop_server (void);
105
106/**
107 * Cadet channel for creating outbound channels.
108 */
109extern struct GNUNET_CADET_Handle *cadet_handle;
110
111/**
112 * Map from peer identities to 'struct CadetHandles' with cadet
113 * channels to those peers.
114 */
115extern struct GNUNET_CONTAINER_MultiPeerMap *cadet_map;
116
117
118GNUNET_NETWORK_STRUCT_BEGIN
119
120/**
121 * Query from one peer, asking the other for CHK-data.
122 */
123struct CadetQueryMessage
124{
125 /**
126 * Type is GNUNET_MESSAGE_TYPE_FS_CADET_QUERY.
127 */
128 struct GNUNET_MessageHeader header;
129
130 /**
131 * Block type must be DBLOCK or IBLOCK.
132 */
133 uint32_t type GNUNET_PACKED;
134
135 /**
136 * Query hash from CHK (hash of encrypted block).
137 */
138 struct GNUNET_HashCode query;
139};
140
141
142/**
143 * Reply to a CadetQueryMessage.
144 */
145struct CadetReplyMessage
146{
147 /**
148 * Type is GNUNET_MESSAGE_TYPE_FS_CADET_REPLY.
149 */
150 struct GNUNET_MessageHeader header;
151
152 /**
153 * Block type must be DBLOCK or IBLOCK.
154 */
155 uint32_t type GNUNET_PACKED;
156
157 /**
158 * Expiration time for the block.
159 */
160 struct GNUNET_TIME_AbsoluteNBO expiration;
161
162 /* followed by the encrypted block */
163};
164
165GNUNET_NETWORK_STRUCT_END
166
167
168#endif
diff --git a/src/fs/gnunet-service-fs_cadet_client.c b/src/fs/gnunet-service-fs_cadet_client.c
deleted file mode 100644
index 398fcd604..000000000
--- a/src/fs/gnunet-service-fs_cadet_client.c
+++ /dev/null
@@ -1,728 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_cadet_client.c
23 * @brief non-anonymous file-transfer
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - PORT is set to old application type, unsure if we should keep
28 * it that way (fine for now)
29 */
30#include "platform.h"
31#include "gnunet_constants.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_cadet_service.h"
34#include "gnunet_protocols.h"
35#include "gnunet_applications.h"
36#include "gnunet-service-fs.h"
37#include "gnunet-service-fs_indexing.h"
38#include "gnunet-service-fs_cadet.h"
39
40
41/**
42 * After how long do we reset connections without replies?
43 */
44#define CLIENT_RETRY_TIMEOUT \
45 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
46
47
48/**
49 * Handle for a cadet to another peer.
50 */
51struct CadetHandle;
52
53
54/**
55 * Handle for a request that is going out via cadet API.
56 */
57struct GSF_CadetRequest
58{
59 /**
60 * DLL.
61 */
62 struct GSF_CadetRequest *next;
63
64 /**
65 * DLL.
66 */
67 struct GSF_CadetRequest *prev;
68
69 /**
70 * Which cadet is this request associated with?
71 */
72 struct CadetHandle *mh;
73
74 /**
75 * Function to call with the result.
76 */
77 GSF_CadetReplyProcessor proc;
78
79 /**
80 * Closure for @e proc
81 */
82 void *proc_cls;
83
84 /**
85 * Query to transmit to the other peer.
86 */
87 struct GNUNET_HashCode query;
88
89 /**
90 * Desired type for the reply.
91 */
92 enum GNUNET_BLOCK_Type type;
93
94 /**
95 * Did we transmit this request already? #GNUNET_YES if we are
96 * in the 'waiting_map', #GNUNET_NO if we are in the 'pending' DLL.
97 */
98 int was_transmitted;
99};
100
101
102/**
103 * Handle for a cadet to another peer.
104 */
105struct CadetHandle
106{
107 /**
108 * Head of DLL of pending requests on this cadet.
109 */
110 struct GSF_CadetRequest *pending_head;
111
112 /**
113 * Tail of DLL of pending requests on this cadet.
114 */
115 struct GSF_CadetRequest *pending_tail;
116
117 /**
118 * Map from query to `struct GSF_CadetRequest`s waiting for
119 * a reply.
120 */
121 struct GNUNET_CONTAINER_MultiHashMap *waiting_map;
122
123 /**
124 * Channel to the other peer.
125 */
126 struct GNUNET_CADET_Channel *channel;
127
128 /**
129 * Which peer does this cadet go to?
130 */
131 struct GNUNET_PeerIdentity target;
132
133 /**
134 * Task to kill inactive cadets (we keep them around for
135 * a few seconds to give the application a chance to give
136 * us another query).
137 */
138 struct GNUNET_SCHEDULER_Task *timeout_task;
139
140 /**
141 * Task to reset cadets that had errors (asynchronously,
142 * as we may not be able to do it immediately during a
143 * callback from the cadet API).
144 */
145 struct GNUNET_SCHEDULER_Task *reset_task;
146};
147
148
149/**
150 * Cadet channel for creating outbound channels.
151 */
152struct GNUNET_CADET_Handle *cadet_handle;
153
154/**
155 * Map from peer identities to 'struct CadetHandles' with cadet
156 * channels to those peers.
157 */
158struct GNUNET_CONTAINER_MultiPeerMap *cadet_map;
159
160
161/* ********************* client-side code ************************* */
162
163
164/**
165 * Transmit pending requests via the cadet.
166 *
167 * @param cls `struct CadetHandle` to process
168 */
169static void
170transmit_pending (void *cls);
171
172
173/**
174 * Iterator called on each entry in a waiting map to
175 * move it back to the pending list.
176 *
177 * @param cls the `struct CadetHandle`
178 * @param key the key of the entry in the map (the query)
179 * @param value the `struct GSF_CadetRequest` to move to pending
180 * @return #GNUNET_YES (continue to iterate)
181 */
182static int
183move_to_pending (void *cls, const struct GNUNET_HashCode *key, void *value)
184{
185 struct CadetHandle *mh = cls;
186 struct GSF_CadetRequest *sr = value;
187
188 GNUNET_assert (
189 GNUNET_YES ==
190 GNUNET_CONTAINER_multihashmap_remove (mh->waiting_map, key, value));
191 GNUNET_CONTAINER_DLL_insert (mh->pending_head, mh->pending_tail, sr);
192 sr->was_transmitted = GNUNET_NO;
193 return GNUNET_YES;
194}
195
196
197/**
198 * Functions with this signature are called whenever a complete reply
199 * is received.
200 *
201 * @param cls closure with the `struct CadetHandle`
202 * @param srm the actual message
203 * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
204 */
205static int
206check_reply (void *cls, const struct CadetReplyMessage *srm)
207{
208 /* We check later... */
209 return GNUNET_OK;
210}
211
212
213/**
214 * Task called when it is time to reset an cadet.
215 *
216 * @param cls the `struct CadetHandle` to tear down
217 */
218static void
219reset_cadet_task (void *cls);
220
221
222/**
223 * We had a serious error, tear down and re-create cadet from scratch,
224 * but do so asynchronously.
225 *
226 * @param mh cadet to reset
227 */
228static void
229reset_cadet_async (struct CadetHandle *mh)
230{
231 if (NULL != mh->reset_task)
232 GNUNET_SCHEDULER_cancel (mh->reset_task);
233 mh->reset_task = GNUNET_SCHEDULER_add_now (&reset_cadet_task, mh);
234}
235
236
237/**
238 * Closure for handle_reply().
239 */
240struct HandleReplyClosure
241{
242 /**
243 * Reply payload.
244 */
245 const void *data;
246
247 /**
248 * Expiration time for the block.
249 */
250 struct GNUNET_TIME_Absolute expiration;
251
252 /**
253 * Number of bytes in @e data.
254 */
255 size_t data_size;
256
257 /**
258 * Type of the block.
259 */
260 enum GNUNET_BLOCK_Type type;
261
262 /**
263 * Did we have a matching query?
264 */
265 int found;
266};
267
268
269/**
270 * Iterator called on each entry in a waiting map to
271 * process a result.
272 *
273 * @param cls the `struct HandleReplyClosure`
274 * @param key the key of the entry in the map (the query)
275 * @param value the `struct GSF_CadetRequest` to handle result for
276 * @return #GNUNET_YES (continue to iterate)
277 */
278static int
279process_reply (void *cls, const struct GNUNET_HashCode *key, void *value)
280{
281 struct HandleReplyClosure *hrc = cls;
282 struct GSF_CadetRequest *sr = value;
283
284 sr->proc (sr->proc_cls,
285 hrc->type,
286 hrc->expiration,
287 hrc->data_size,
288 hrc->data);
289 sr->proc = NULL;
290 GSF_cadet_query_cancel (sr);
291 hrc->found = GNUNET_YES;
292 return GNUNET_YES;
293}
294
295
296/**
297 * Iterator called on each entry in a waiting map to
298 * call the 'proc' continuation and release associated
299 * resources.
300 *
301 * @param cls the `struct CadetHandle`
302 * @param key the key of the entry in the map (the query)
303 * @param value the `struct GSF_CadetRequest` to clean up
304 * @return #GNUNET_YES (continue to iterate)
305 */
306static int
307free_waiting_entry (void *cls, const struct GNUNET_HashCode *key, void *value)
308{
309 struct GSF_CadetRequest *sr = value;
310
311 GSF_cadet_query_cancel (sr);
312 return GNUNET_YES;
313}
314
315
316/**
317 * Functions with this signature are called whenever a complete reply
318 * is received.
319 *
320 * @param cls closure with the `struct CadetHandle`
321 * @param srm the actual message
322 */
323static void
324handle_reply (void *cls, const struct CadetReplyMessage *srm)
325{
326 struct CadetHandle *mh = cls;
327 struct HandleReplyClosure hrc;
328 uint16_t msize;
329 enum GNUNET_BLOCK_Type type;
330 struct GNUNET_HashCode query;
331
332 msize = ntohs (srm->header.size) - sizeof(struct CadetReplyMessage);
333 type = (enum GNUNET_BLOCK_Type) ntohl (srm->type);
334 if (GNUNET_YES !=
335 GNUNET_BLOCK_get_key (GSF_block_ctx, type, &srm[1], msize, &query))
336 {
337 GNUNET_break_op (0);
338 GNUNET_log (
339 GNUNET_ERROR_TYPE_WARNING,
340 "Received bogus reply of type %u with %u bytes via cadet from peer %s\n",
341 type,
342 msize,
343 GNUNET_i2s (&mh->target));
344 reset_cadet_async (mh);
345 return;
346 }
347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
348 "Received reply `%s' via cadet from peer %s\n",
349 GNUNET_h2s (&query),
350 GNUNET_i2s (&mh->target));
351 GNUNET_CADET_receive_done (mh->channel);
352 GNUNET_STATISTICS_update (GSF_stats,
353 gettext_noop ("# replies received via cadet"),
354 1,
355 GNUNET_NO);
356 hrc.data = &srm[1];
357 hrc.data_size = msize;
358 hrc.expiration = GNUNET_TIME_absolute_ntoh (srm->expiration);
359 hrc.type = type;
360 hrc.found = GNUNET_NO;
361 GNUNET_CONTAINER_multihashmap_get_multiple (mh->waiting_map,
362 &query,
363 &process_reply,
364 &hrc);
365 if (GNUNET_NO == hrc.found)
366 {
367 GNUNET_STATISTICS_update (GSF_stats,
368 gettext_noop (
369 "# replies received via cadet dropped"),
370 1,
371 GNUNET_NO);
372 }
373}
374
375
376/**
377 * Function called by cadet when a client disconnects.
378 * Cleans up our `struct CadetClient` of that channel.
379 *
380 * @param cls our `struct CadetClient`
381 * @param channel channel of the disconnecting client
382 */
383static void
384disconnect_cb (void *cls, const struct GNUNET_CADET_Channel *channel)
385{
386 struct CadetHandle *mh = cls;
387 struct GSF_CadetRequest *sr;
388
389 if (NULL == mh->channel)
390 return; /* being destroyed elsewhere */
391 GNUNET_assert (channel == mh->channel);
392 mh->channel = NULL;
393 while (NULL != (sr = mh->pending_head))
394 GSF_cadet_query_cancel (sr);
395 /* first remove `mh` from the `cadet_map`, so that if the
396 callback from `free_waiting_entry()` happens to re-issue
397 the request, we don't immediately have it back in the
398 `waiting_map`. */
399 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multipeermap_remove (cadet_map,
400 &mh->target,
401 mh));
402 GNUNET_CONTAINER_multihashmap_iterate (mh->waiting_map,
403 &free_waiting_entry,
404 mh);
405 if (NULL != mh->timeout_task)
406 GNUNET_SCHEDULER_cancel (mh->timeout_task);
407 if (NULL != mh->reset_task)
408 GNUNET_SCHEDULER_cancel (mh->reset_task);
409 GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (mh->waiting_map));
410 GNUNET_CONTAINER_multihashmap_destroy (mh->waiting_map);
411 GNUNET_free (mh);
412}
413
414
415/**
416 * Function called whenever an MQ-channel's transmission window size changes.
417 *
418 * The first callback in an outgoing channel will be with a non-zero value
419 * and will mean the channel is connected to the destination.
420 *
421 * For an incoming channel it will be called immediately after the
422 * #GNUNET_CADET_ConnectEventHandler, also with a non-zero value.
423 *
424 * @param cls Channel closure.
425 * @param channel Connection to the other end (henceforth invalid).
426 * @param window_size New window size. If the is more messages than buffer size
427 * this value will be negative..
428 */
429static void
430window_change_cb (void *cls,
431 const struct GNUNET_CADET_Channel *channel,
432 int window_size)
433{
434 /* FIXME: for flow control, implement? */
435#if 0
436 /* Something like this instead of the GNUNET_MQ_notify_sent() in
437 transmit_pending() might be good (once the window change CB works...) */
438 if (0 < window_size) /* test needed? */
439 transmit_pending (mh);
440#endif
441}
442
443
444/**
445 * We had a serious error, tear down and re-create cadet from scratch.
446 *
447 * @param mh cadet to reset
448 */
449static void
450reset_cadet (struct CadetHandle *mh)
451{
452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453 "Resetting cadet channel to %s\n",
454 GNUNET_i2s (&mh->target));
455 if (NULL != mh->channel)
456 {
457 GNUNET_CADET_channel_destroy (mh->channel);
458 mh->channel = NULL;
459 }
460 GNUNET_CONTAINER_multihashmap_iterate (mh->waiting_map, &move_to_pending, mh);
461 {
462 struct GNUNET_MQ_MessageHandler handlers[] =
463 { GNUNET_MQ_hd_var_size (reply,
464 GNUNET_MESSAGE_TYPE_FS_CADET_REPLY,
465 struct CadetReplyMessage,
466 mh),
467 GNUNET_MQ_handler_end () };
468 struct GNUNET_HashCode port;
469
470 GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER,
471 strlen (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER),
472 &port);
473 mh->channel = GNUNET_CADET_channel_create (cadet_handle,
474 mh,
475 &mh->target,
476 &port,
477 &window_change_cb,
478 &disconnect_cb,
479 handlers);
480 }
481 transmit_pending (mh);
482}
483
484
485/**
486 * Task called when it is time to destroy an inactive cadet channel.
487 *
488 * @param cls the `struct CadetHandle` to tear down
489 */
490static void
491cadet_timeout (void *cls)
492{
493 struct CadetHandle *mh = cls;
494 struct GNUNET_CADET_Channel *tun;
495
496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497 "Timeout on cadet channel to %s\n",
498 GNUNET_i2s (&mh->target));
499 mh->timeout_task = NULL;
500 tun = mh->channel;
501 mh->channel = NULL;
502 if (NULL != tun)
503 GNUNET_CADET_channel_destroy (tun);
504}
505
506
507/**
508 * Task called when it is time to reset an cadet.
509 *
510 * @param cls the `struct CadetHandle` to tear down
511 */
512static void
513reset_cadet_task (void *cls)
514{
515 struct CadetHandle *mh = cls;
516
517 mh->reset_task = NULL;
518 reset_cadet (mh);
519}
520
521
522/**
523 * Transmit pending requests via the cadet.
524 *
525 * @param cls `struct CadetHandle` to process
526 */
527static void
528transmit_pending (void *cls)
529{
530 struct CadetHandle *mh = cls;
531 struct GNUNET_MQ_Handle *mq = GNUNET_CADET_get_mq (mh->channel);
532 struct GSF_CadetRequest *sr;
533 struct GNUNET_MQ_Envelope *env;
534 struct CadetQueryMessage *sqm;
535
536 if ((0 != GNUNET_MQ_get_length (mq)) || (NULL == (sr = mh->pending_head)))
537 return;
538 GNUNET_CONTAINER_DLL_remove (mh->pending_head, mh->pending_tail, sr);
539 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (
540 mh->waiting_map,
541 &sr->query,
542 sr,
543 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
544 sr->was_transmitted = GNUNET_YES;
545 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
546 "Sending query for %s via cadet to %s\n",
547 GNUNET_h2s (&sr->query),
548 GNUNET_i2s (&mh->target));
549 env = GNUNET_MQ_msg (sqm, GNUNET_MESSAGE_TYPE_FS_CADET_QUERY);
550 GNUNET_MQ_env_set_options (env,
551 GNUNET_MQ_PREF_GOODPUT
552 | GNUNET_MQ_PREF_CORK_ALLOWED
553 | GNUNET_MQ_PREF_OUT_OF_ORDER);
554 sqm->type = htonl (sr->type);
555 sqm->query = sr->query;
556 GNUNET_MQ_notify_sent (env, &transmit_pending, mh);
557 GNUNET_MQ_send (mq, env);
558}
559
560
561/**
562 * Get (or create) a cadet to talk to the given peer.
563 *
564 * @param target peer we want to communicate with
565 */
566static struct CadetHandle *
567get_cadet (const struct GNUNET_PeerIdentity *target)
568{
569 struct CadetHandle *mh;
570
571 mh = GNUNET_CONTAINER_multipeermap_get (cadet_map, target);
572 if (NULL != mh)
573 {
574 if (NULL != mh->timeout_task)
575 {
576 GNUNET_SCHEDULER_cancel (mh->timeout_task);
577 mh->timeout_task = NULL;
578 }
579 return mh;
580 }
581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
582 "Creating cadet channel to %s\n",
583 GNUNET_i2s (target));
584 mh = GNUNET_new (struct CadetHandle);
585 mh->reset_task =
586 GNUNET_SCHEDULER_add_delayed (CLIENT_RETRY_TIMEOUT, &reset_cadet_task, mh);
587 mh->waiting_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_YES);
588 mh->target = *target;
589 GNUNET_assert (GNUNET_OK ==
590 GNUNET_CONTAINER_multipeermap_put (
591 cadet_map,
592 &mh->target,
593 mh,
594 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
595 {
596 struct GNUNET_MQ_MessageHandler handlers[] =
597 { GNUNET_MQ_hd_var_size (reply,
598 GNUNET_MESSAGE_TYPE_FS_CADET_REPLY,
599 struct CadetReplyMessage,
600 mh),
601 GNUNET_MQ_handler_end () };
602 struct GNUNET_HashCode port;
603
604 GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER,
605 strlen (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER),
606 &port);
607 mh->channel = GNUNET_CADET_channel_create (cadet_handle,
608 mh,
609 &mh->target,
610 &port,
611 &window_change_cb,
612 &disconnect_cb,
613 handlers);
614 }
615 return mh;
616}
617
618
619/**
620 * Look for a block by directly contacting a particular peer.
621 *
622 * @param target peer that should have the block
623 * @param query hash to query for the block
624 * @param type desired type for the block
625 * @param proc function to call with result
626 * @param proc_cls closure for @a proc
627 * @return handle to cancel the operation
628 */
629struct GSF_CadetRequest *
630GSF_cadet_query (const struct GNUNET_PeerIdentity *target,
631 const struct GNUNET_HashCode *query,
632 enum GNUNET_BLOCK_Type type,
633 GSF_CadetReplyProcessor proc,
634 void *proc_cls)
635{
636 struct CadetHandle *mh;
637 struct GSF_CadetRequest *sr;
638
639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640 "Preparing to send query for %s via cadet to %s\n",
641 GNUNET_h2s (query),
642 GNUNET_i2s (target));
643 mh = get_cadet (target);
644 sr = GNUNET_new (struct GSF_CadetRequest);
645 sr->mh = mh;
646 sr->proc = proc;
647 sr->proc_cls = proc_cls;
648 sr->type = type;
649 sr->query = *query;
650 GNUNET_CONTAINER_DLL_insert (mh->pending_head, mh->pending_tail, sr);
651 transmit_pending (mh);
652 return sr;
653}
654
655
656/**
657 * Cancel an active request; must not be called after 'proc'
658 * was called.
659 *
660 * @param sr request to cancel
661 */
662void
663GSF_cadet_query_cancel (struct GSF_CadetRequest *sr)
664{
665 struct CadetHandle *mh = sr->mh;
666 GSF_CadetReplyProcessor p;
667
668 p = sr->proc;
669 sr->proc = NULL;
670 if (NULL != p)
671 {
672 /* signal failure / cancellation to callback */
673 p (sr->proc_cls, GNUNET_BLOCK_TYPE_ANY, GNUNET_TIME_UNIT_ZERO_ABS, 0, NULL);
674 }
675 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
676 "Cancelled query for %s via cadet to %s\n",
677 GNUNET_h2s (&sr->query),
678 GNUNET_i2s (&sr->mh->target));
679 if (GNUNET_YES == sr->was_transmitted)
680 GNUNET_assert (
681 GNUNET_OK ==
682 GNUNET_CONTAINER_multihashmap_remove (mh->waiting_map, &sr->query, sr));
683 else
684 GNUNET_CONTAINER_DLL_remove (mh->pending_head, mh->pending_tail, sr);
685 GNUNET_free (sr);
686 if ((0 == GNUNET_CONTAINER_multihashmap_size (mh->waiting_map)) &&
687 (NULL == mh->pending_head))
688 mh->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
689 &cadet_timeout,
690 mh);
691}
692
693
694/**
695 * Function called on each active cadets to shut them down.
696 *
697 * @param cls NULL
698 * @param key target peer, unused
699 * @param value the `struct CadetHandle` to destroy
700 * @return #GNUNET_YES (continue to iterate)
701 */
702int
703GSF_cadet_release_clients (void *cls,
704 const struct GNUNET_PeerIdentity *key,
705 void *value)
706{
707 struct CadetHandle *mh = value;
708
709 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
710 "Timeout on cadet channel to %s\n",
711 GNUNET_i2s (&mh->target));
712 if (NULL != mh->channel)
713 {
714 struct GNUNET_CADET_Channel *channel = mh->channel;
715
716 mh->channel = NULL;
717 GNUNET_CADET_channel_destroy (channel);
718 }
719 if (NULL != mh->reset_task)
720 {
721 GNUNET_SCHEDULER_cancel (mh->reset_task);
722 mh->reset_task = NULL;
723 }
724 return GNUNET_YES;
725}
726
727
728/* end of gnunet-service-fs_cadet_client.c */
diff --git a/src/fs/gnunet-service-fs_cadet_server.c b/src/fs/gnunet-service-fs_cadet_server.c
deleted file mode 100644
index 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 */
49struct WriteQueueItem
50{
51 /**
52 * Kept in a DLL.
53 */
54 struct WriteQueueItem *next;
55
56 /**
57 * Kept in a DLL.
58 */
59 struct WriteQueueItem *prev;
60
61 /**
62 * Number of bytes of payload, allocated at the end of this struct.
63 */
64 size_t msize;
65};
66
67
68/**
69 * Information we keep around for each active cadeting client.
70 */
71struct CadetClient
72{
73 /**
74 * DLL
75 */
76 struct CadetClient *next;
77
78 /**
79 * DLL
80 */
81 struct CadetClient *prev;
82
83 /**
84 * Channel for communication.
85 */
86 struct GNUNET_CADET_Channel *channel;
87
88 /**
89 * Head of write queue.
90 */
91 struct WriteQueueItem *wqi_head;
92
93 /**
94 * Tail of write queue.
95 */
96 struct WriteQueueItem *wqi_tail;
97
98 /**
99 * Current active request to the datastore, if we have one pending.
100 */
101 struct GNUNET_DATASTORE_QueueEntry *qe;
102
103 /**
104 * Task that is scheduled to asynchronously terminate the connection.
105 */
106 struct GNUNET_SCHEDULER_Task *terminate_task;
107
108 /**
109 * Task that is scheduled to terminate idle connections.
110 */
111 struct GNUNET_SCHEDULER_Task *timeout_task;
112
113 /**
114 * Size of the last write that was initiated.
115 */
116 size_t reply_size;
117};
118
119
120/**
121 * Listen port for incoming requests.
122 */
123static struct GNUNET_CADET_Port *cadet_port;
124
125/**
126 * Head of DLL of cadet clients.
127 */
128static struct CadetClient *sc_head;
129
130/**
131 * Tail of DLL of cadet clients.
132 */
133static struct CadetClient *sc_tail;
134
135/**
136 * Number of active cadet clients in the 'sc_*'-DLL.
137 */
138static unsigned int sc_count;
139
140/**
141 * Maximum allowed number of cadet clients.
142 */
143static unsigned long long sc_count_max;
144
145
146/**
147 * Task run to asynchronously terminate the cadet due to timeout.
148 *
149 * @param cls the 'struct CadetClient'
150 */
151static void
152timeout_cadet_task (void *cls)
153{
154 struct CadetClient *sc = cls;
155 struct GNUNET_CADET_Channel *tun;
156
157 sc->timeout_task = NULL;
158 tun = sc->channel;
159 sc->channel = NULL;
160 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
161 "Timeout for inactive cadet client %p\n",
162 sc);
163 GNUNET_CADET_channel_destroy (tun);
164}
165
166
167/**
168 * Reset the timeout for the cadet client (due to activity).
169 *
170 * @param sc client handle to reset timeout for
171 */
172static void
173refresh_timeout_task (struct CadetClient *sc)
174{
175 if (NULL != sc->timeout_task)
176 GNUNET_SCHEDULER_cancel (sc->timeout_task);
177 sc->timeout_task = GNUNET_SCHEDULER_add_delayed (IDLE_TIMEOUT,
178 &timeout_cadet_task,
179 sc);
180}
181
182
183/**
184 * Check if we are done with the write queue, and if so tell CADET
185 * that we are ready to read more.
186 *
187 * @param cls where to process the write queue
188 */
189static void
190continue_writing (void *cls)
191{
192 struct CadetClient *sc = cls;
193 struct GNUNET_MQ_Handle *mq;
194
195 mq = GNUNET_CADET_get_mq (sc->channel);
196 if (0 != GNUNET_MQ_get_length (mq))
197 {
198 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
199 "Write pending, waiting for it to complete\n");
200 return;
201 }
202 refresh_timeout_task (sc);
203 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
204 "Finished processing cadet request from client %p, ready to receive the next one\n",
205 sc);
206 GNUNET_CADET_receive_done (sc->channel);
207}
208
209
210/**
211 * Process a datum that was stored in the datastore.
212 *
213 * @param cls closure with the `struct CadetClient` which sent the query
214 * @param key key for the content
215 * @param size number of bytes in @a data
216 * @param data content stored
217 * @param type type of the content
218 * @param priority priority of the content
219 * @param anonymity anonymity-level for the content
220 * @param replication replication-level for the content
221 * @param expiration expiration time for the content
222 * @param uid unique identifier for the datum;
223 * maybe 0 if no unique identifier is available
224 */
225static void
226handle_datastore_reply (void *cls,
227 const struct GNUNET_HashCode *key,
228 size_t size,
229 const void *data,
230 enum GNUNET_BLOCK_Type type,
231 uint32_t priority,
232 uint32_t anonymity,
233 uint32_t replication,
234 struct GNUNET_TIME_Absolute expiration,
235 uint64_t uid)
236{
237 struct CadetClient *sc = cls;
238 size_t msize = size + sizeof(struct CadetReplyMessage);
239 struct GNUNET_MQ_Envelope *env;
240 struct CadetReplyMessage *srm;
241
242 sc->qe = NULL;
243 if (NULL == data)
244 {
245 /* no result, this should not really happen, as for
246 non-anonymous routing only peers that HAVE the
247 answers should be queried; OTOH, this is not a
248 hard error as we might have had the answer in the
249 past and the user might have unindexed it. Hence
250 we log at level "INFO" for now. */if (NULL == key)
251 {
252 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
253 "Have no answer and the query was NULL\n");
254 }
255 else
256 {
257 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
258 "Have no answer for query `%s'\n",
259 GNUNET_h2s (key));
260 }
261 GNUNET_STATISTICS_update (GSF_stats,
262 gettext_noop (
263 "# queries received via CADET not answered"),
264 1,
265 GNUNET_NO);
266 continue_writing (sc);
267 return;
268 }
269 if (GNUNET_BLOCK_TYPE_FS_ONDEMAND == type)
270 {
271 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
272 "Performing on-demand encoding for query %s\n",
273 GNUNET_h2s (key));
274 if (GNUNET_OK !=
275 GNUNET_FS_handle_on_demand_block (key,
276 size,
277 data,
278 type,
279 priority,
280 anonymity,
281 replication,
282 expiration,
283 uid,
284 &handle_datastore_reply,
285 sc))
286 {
287 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
288 "On-demand encoding request failed\n");
289 continue_writing (sc);
290 }
291 return;
292 }
293 if (msize > GNUNET_MAX_MESSAGE_SIZE)
294 {
295 GNUNET_break (0);
296 continue_writing (sc);
297 return;
298 }
299 GNUNET_break (GNUNET_BLOCK_TYPE_ANY != type);
300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
301 "Starting transmission of %u byte reply of type %d for query `%s' via cadet to %p\n",
302 (unsigned int) size,
303 (unsigned int) type,
304 GNUNET_h2s (key),
305 sc);
306 env = GNUNET_MQ_msg_extra (srm,
307 size,
308 GNUNET_MESSAGE_TYPE_FS_CADET_REPLY);
309 srm->type = htonl (type);
310 srm->expiration = GNUNET_TIME_absolute_hton (expiration);
311 GNUNET_memcpy (&srm[1],
312 data,
313 size);
314 GNUNET_MQ_notify_sent (env,
315 &continue_writing,
316 sc);
317 GNUNET_STATISTICS_update (GSF_stats,
318 gettext_noop ("# Blocks transferred via cadet"),
319 1,
320 GNUNET_NO);
321 GNUNET_MQ_send (GNUNET_CADET_get_mq (sc->channel),
322 env);
323}
324
325
326/**
327 * Functions with this signature are called whenever a
328 * complete query message is received.
329 *
330 * @param cls closure with the `struct CadetClient`
331 * @param sqm the actual message
332 */
333static void
334handle_request (void *cls,
335 const struct CadetQueryMessage *sqm)
336{
337 struct CadetClient *sc = cls;
338
339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
340 "Received query for `%s' via cadet from client %p\n",
341 GNUNET_h2s (&sqm->query),
342 sc);
343 GNUNET_STATISTICS_update (GSF_stats,
344 gettext_noop ("# queries received via cadet"),
345 1,
346 GNUNET_NO);
347 refresh_timeout_task (sc);
348 sc->qe = GNUNET_DATASTORE_get_key (GSF_dsh,
349 0 /* next_uid */,
350 false /* random */,
351 &sqm->query,
352 ntohl (sqm->type),
353 0 /* priority */,
354 GSF_datastore_queue_size,
355 &handle_datastore_reply,
356 sc);
357 if (NULL == sc->qe)
358 {
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360 "Queueing request with datastore failed (queue full?)\n");
361 continue_writing (sc);
362 }
363}
364
365
366/**
367 * Functions of this type are called upon new cadet connection from other peers.
368 *
369 * @param cls the closure from GNUNET_CADET_connect
370 * @param channel the channel representing the cadet
371 * @param initiator the identity of the peer who wants to establish a cadet
372 * with us; NULL on binding error
373 * @return initial channel context (our `struct CadetClient`)
374 */
375static void *
376connect_cb (void *cls,
377 struct GNUNET_CADET_Channel *channel,
378 const struct GNUNET_PeerIdentity *initiator)
379{
380 struct CadetClient *sc;
381
382 GNUNET_assert (NULL != channel);
383 if (sc_count >= sc_count_max)
384 {
385 GNUNET_STATISTICS_update (GSF_stats,
386 gettext_noop (
387 "# cadet client connections rejected"),
388 1,
389 GNUNET_NO);
390 GNUNET_CADET_channel_destroy (channel);
391 return NULL;
392 }
393 GNUNET_STATISTICS_update (GSF_stats,
394 gettext_noop ("# cadet connections active"),
395 1,
396 GNUNET_NO);
397 sc = GNUNET_new (struct CadetClient);
398 sc->channel = channel;
399 GNUNET_CONTAINER_DLL_insert (sc_head,
400 sc_tail,
401 sc);
402 sc_count++;
403 refresh_timeout_task (sc);
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405 "Accepting inbound cadet connection from `%s' as client %p\n",
406 GNUNET_i2s (initiator),
407 sc);
408 return sc;
409}
410
411
412/**
413 * Function called by cadet when a client disconnects.
414 * Cleans up our `struct CadetClient` of that channel.
415 *
416 * @param cls our `struct CadetClient`
417 * @param channel channel of the disconnecting client
418 */
419static void
420disconnect_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 */
470static void
471window_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 */
482void
483GSF_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 */
522void
523GSF_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 */
64struct GSF_PeerTransmitHandle
65{
66 /**
67 * Kept in a doubly-linked list.
68 */
69 struct GSF_PeerTransmitHandle *next;
70
71 /**
72 * Kept in a doubly-linked list.
73 */
74 struct GSF_PeerTransmitHandle *prev;
75
76 /**
77 * Time when this transmission request was issued.
78 */
79 struct GNUNET_TIME_Absolute transmission_request_start_time;
80
81 /**
82 * Envelope with the actual message.
83 */
84 struct GNUNET_MQ_Envelope *env;
85
86 /**
87 * Peer this request targets.
88 */
89 struct GSF_ConnectedPeer *cp;
90
91 /**
92 * #GNUNET_YES if this is a query, #GNUNET_NO for content.
93 */
94 int is_query;
95
96 /**
97 * Priority of this request.
98 */
99 uint32_t priority;
100};
101
102
103/**
104 * Handle for an entry in our delay list.
105 */
106struct 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 */
143struct 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 */
165struct 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 */
266static struct GNUNET_CONTAINER_MultiPeerMap *cp_map;
267
268/**
269 * Handle to peerstore service.
270 */
271static struct GNUNET_PEERSTORE_Handle *peerstore;
272
273/**
274 * Task used to flush respect values to disk.
275 */
276static 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 */
285void
286GSF_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 */
305struct GSF_PeerPerformanceData *
306GSF_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 */
317static void
318peer_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 */
327static void
328schedule_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 */
346static void
347peer_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 */
389static void
390peer_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 */
420static int
421consider_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
446void *
447GSF_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 */
497static void
498revive_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
516struct GSF_ConnectedPeer *
517GSF_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 */
531void
532handle_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 */
563static void
564free_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 */
595static int
596cancel_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 */
615static void
616peer_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 */
635static void
636transmit_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 */
658static struct GNUNET_TIME_Relative
659get_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 */
696static void
697handle_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 */
815static int
816change_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 */
853static int32_t
854bound_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 */
921static int32_t
922bound_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 */
943struct 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 */
977static int
978test_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 */
1023void
1024handle_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 */
1221void
1222GSF_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 */
1264void
1265GSF_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 */
1288void
1289GSF_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 */
1304void
1305GSF_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 */
1326static int
1327flush_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
1348void
1349GSF_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 */
1425struct 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 */
1447static int
1448call_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
1462void
1463GSF_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 */
1482void
1483GSF_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 */
1497const struct GNUNET_PeerIdentity *
1498GSF_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 */
1512void
1513GSF_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 */
1560void
1561GSF_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 */
1573static void
1574cron_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 */
1590void
1591GSF_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 */
1603void
1604GSF_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 */
1626static int
1627clean_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 */
1648void
1649GSF_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 */
63struct GSF_PeerPerformanceData
64{
65 /**
66 * List of the last clients for which this peer successfully
67 * answered a query.
68 */
69 struct GSF_LocalClient *last_client_replies[CS2P_SUCCESS_LIST_SIZE];
70
71 /**
72 * List of the last PIDs for which
73 * this peer successfully answered a query;
74 * We use 0 to indicate no successful reply.
75 */
76 GNUNET_PEER_Id last_p2p_replies[P2P_SUCCESS_LIST_SIZE];
77
78 /**
79 * Average delay between sending the peer a request and
80 * getting a reply (only calculated over the requests for
81 * which we actually got a reply). Calculated
82 * as a moving average: new_delay = ((n-1)*last_delay+curr_delay) / n
83 */
84 struct GNUNET_TIME_Relative avg_reply_delay;
85
86 /**
87 * If we get content we already have from this peer, for how
88 * long do we block it? Adjusted based on the fraction of
89 * redundant data we receive, between 1s and 1h.
90 */
91 struct GNUNET_TIME_Relative migration_delay;
92
93 /**
94 * Point in time until which this peer does not want us to migrate content
95 * to it.
96 */
97 struct GNUNET_TIME_Absolute migration_blocked_until;
98
99 /**
100 * Transmission times for the last MAX_QUEUE_PER_PEER
101 * requests for this peer. Used as a ring buffer, current
102 * offset is stored in 'last_request_times_off'. If the
103 * oldest entry is more recent than the 'avg_delay', we should
104 * not send any more requests right now.
105 */
106 struct GNUNET_TIME_Absolute last_request_times[MAX_QUEUE_PER_PEER];
107
108 /**
109 * How long does it typically take for us to transmit a message
110 * to this peer? (delay between the request being issued and
111 * the callback being invoked).
112 */
113 struct GNUNET_LOAD_Value *transmission_delay;
114
115 /**
116 * Average priority of successful replies. Calculated
117 * as a moving average: new_avg = ((n-1)*last_avg+curr_prio) / n
118 */
119 double avg_priority;
120
121 /**
122 * The peer's identity (interned version).
123 */
124 GNUNET_PEER_Id pid;
125
126 /**
127 * The peer's identity (pointer).
128 */
129 const struct GNUNET_PeerIdentity *peer;
130
131 /**
132 * Respect rating for this peer
133 */
134 uint32_t respect;
135
136 /**
137 * Number of pending queries (replies are not counted)
138 */
139 unsigned int pending_queries;
140
141 /**
142 * Number of pending replies (queries are not counted)
143 */
144 unsigned int pending_replies;
145};
146
147
148/**
149 * Signature of function called on a connected peer.
150 *
151 * @param cls closure
152 * @param peer identity of the peer
153 * @param cp handle to the connected peer record
154 * @param perf peer performance data
155 */
156typedef void
157(*GSF_ConnectedPeerIterator) (void *cls,
158 const struct GNUNET_PeerIdentity *peer,
159 struct GSF_ConnectedPeer *cp,
160 const struct GSF_PeerPerformanceData *ppd);
161
162
163/**
164 * Function called to get a message for transmission.
165 *
166 * @param cls closure
167 * @param buf_size number of bytes available in @a buf
168 * @param buf where to copy the message, NULL on error (peer disconnect)
169 * @return number of bytes copied to @a buf, can be 0 (without indicating an error)
170 */
171typedef size_t
172(*GSF_GetMessageCallback) (void *cls,
173 size_t buf_size,
174 void *buf);
175
176
177/**
178 * Signature of function called on a reservation success or failure.
179 *
180 * @param cls closure
181 * @param cp handle to the connected peer record
182 * @param success #GNUNET_YES on success, #GNUNET_NO on failure
183 */
184typedef void
185(*GSF_PeerReserveCallback) (void *cls,
186 struct GSF_ConnectedPeer *cp,
187 int success);
188
189
190/**
191 * Handle to cancel a transmission request.
192 */
193struct GSF_PeerTransmitHandle;
194
195
196/**
197 * A peer connected to us. Setup the connected peer
198 * records.
199 *
200 * @param cls NULL
201 * @param peer identity of peer that connected
202 * @param mq message queue for talking to @a peer
203 * @return internal handle for the peer
204 */
205void *
206GSF_peer_connect_handler (void *cls,
207 const struct GNUNET_PeerIdentity *peer,
208 struct GNUNET_MQ_Handle *mq);
209
210
211/**
212 * Get a handle for a connected peer.
213 *
214 * @param peer peer's identity
215 * @return NULL if this peer is not currently connected
216 */
217struct GSF_ConnectedPeer *
218GSF_peer_get_ (const struct GNUNET_PeerIdentity *peer);
219
220
221/**
222 * Update the latency information kept for the given peer.
223 *
224 * @param id peer record to update
225 * @param latency current latency value
226 */
227void
228GSF_update_peer_latency_ (const struct GNUNET_PeerIdentity *id,
229 struct GNUNET_TIME_Relative latency);
230
231
232/**
233 * Transmit a message to the given peer as soon as possible.
234 * If the peer disconnects before the transmission can happen,
235 * the callback is invoked with a 'NULL' buffer.
236 *
237 * @param cp target peer
238 * @param is_query is this a query (#GNUNET_YES) or content (#GNUNET_NO)
239 * @param priority how important is this request?
240 * @param env envelope of message to send
241 */
242void
243GSF_peer_transmit_ (struct GSF_ConnectedPeer *cp,
244 int is_query,
245 uint32_t priority,
246 struct GNUNET_MQ_Envelope *env);
247
248
249/**
250 * Report on receiving a reply; update the performance record of the given peer.
251 *
252 * @param cp responding peer (will be updated)
253 * @param request_time time at which the original query was transmitted
254 * @param request_priority priority of the original request
255 */
256void
257GSF_peer_update_performance_ (struct GSF_ConnectedPeer *cp,
258 struct GNUNET_TIME_Absolute request_time,
259 uint32_t request_priority);
260
261
262/**
263 * Report on receiving a reply in response to an initiating client.
264 * Remember that this peer is good for this client.
265 *
266 * @param cp responding peer (will be updated)
267 * @param initiator_client local client on responsible for query
268 */
269void
270GSF_peer_update_responder_client_ (struct GSF_ConnectedPeer *cp,
271 struct GSF_LocalClient *initiator_client);
272
273
274/**
275 * Report on receiving a reply in response to an initiating peer.
276 * Remember that this peer is good for this initiating peer.
277 *
278 * @param cp responding peer (will be updated)
279 * @param initiator_peer other peer responsible for query
280 */
281void
282GSF_peer_update_responder_peer_ (struct GSF_ConnectedPeer *cp,
283 const struct GSF_ConnectedPeer
284 *initiator_peer);
285
286
287/**
288 * Handle P2P #GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP message.
289 *
290 * @param cls closure, the `struct GSF_ConnectedPeer`
291 * @param msm the actual message
292 */
293void
294handle_p2p_migration_stop (void *cls,
295 const struct MigrationStopMessage *message);
296
297
298/**
299 * Handle P2P "QUERY" message.
300 *
301 * @param cls the `struct GSF_ConnectedPeer` of the other sender
302 * @param gm the actual message
303 */
304void
305handle_p2p_get (void *cls,
306 const struct GetMessage *gm);
307
308
309/**
310 * Return the performance data record for the given peer
311 *
312 * @param cp peer to query
313 * @return performance data record for the peer
314 */
315struct GSF_PeerPerformanceData *
316GSF_get_peer_performance_data_ (struct GSF_ConnectedPeer *cp);
317
318
319/**
320 * Ask a peer to stop migrating data to us until the given point
321 * in time.
322 *
323 * @param cp peer to ask
324 * @param block_time until when to block
325 */
326void
327GSF_block_peer_migration_ (struct GSF_ConnectedPeer *cp,
328 struct GNUNET_TIME_Absolute block_time);
329
330
331/**
332 * A peer disconnected from us. Tear down the connected peer
333 * record.
334 *
335 * @param cls unused
336 * @param peer identity of peer that disconnected
337 * @param internal_cls the corresponding `struct GSF_ConnectedPeer`
338 */
339void
340GSF_peer_disconnect_handler (void *cls,
341 const struct GNUNET_PeerIdentity *peer,
342 void *internal_cls);
343
344
345/**
346 * Notification that a local client disconnected. Clean up all of our
347 * references to the given handle.
348 *
349 * @param lc handle to the local client (henceforth invalid)
350 */
351void
352GSF_handle_local_client_disconnect_ (const struct GSF_LocalClient *lc);
353
354
355/**
356 * Notify core about a preference we have for the given peer
357 * (to allocate more resources towards it). The change will
358 * be communicated the next time we reserve bandwidth with
359 * core (not instantly).
360 *
361 * @param cp peer to reserve bandwidth from
362 * @param pref preference change
363 */
364void
365GSF_connected_peer_change_preference_ (struct GSF_ConnectedPeer *cp,
366 uint64_t pref);
367
368
369/**
370 * Obtain the identity of a connected peer.
371 *
372 * @param cp peer to get identity of
373 * @param id identity to set (written to)
374 */
375void
376GSF_connected_peer_get_identity_ (const struct GSF_ConnectedPeer *cp,
377 struct GNUNET_PeerIdentity *id);
378
379
380/**
381 * Obtain the identity of a connected peer.
382 *
383 * @param cp peer to get identity of
384 * @return reference to peer identity, valid until peer disconnects (!)
385 */
386const struct GNUNET_PeerIdentity *
387GSF_connected_peer_get_identity2_ (const struct GSF_ConnectedPeer *cp);
388
389
390/**
391 * Iterate over all connected peers.
392 *
393 * @param it function to call for each peer
394 * @param it_cls closure for @a it
395 */
396void
397GSF_iterate_connected_peers_ (GSF_ConnectedPeerIterator it, void *it_cls);
398
399
400/**
401 * Initialize peer management subsystem.
402 */
403void
404GSF_connected_peer_init_ (void);
405
406
407/**
408 * Shutdown peer management subsystem.
409 */
410void
411GSF_connected_peer_done_ (void);
412
413
414#endif
415/* end of gnunet-service-fs_cp.h */
diff --git a/src/fs/gnunet-service-fs_indexing.c b/src/fs/gnunet-service-fs_indexing.c
deleted file mode 100644
index 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 */
41struct 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 */
81static struct IndexInfo *indexed_files_head;
82
83/**
84 * Tail of linked list of indexed files.
85 */
86static 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 */
93static struct GNUNET_CONTAINER_MultiHashMap *ifm;
94
95/**
96 * Our configuration.
97 */
98static 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 */
104static struct GNUNET_DATASTORE_Handle *dsh;
105
106
107/**
108 * Write the current index information list to disk.
109 */
110static void
111write_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 */
158static void
159read_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 */
235static void
236remove_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
248int
249GNUNET_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 */
356void
357GNUNET_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 */
393int
394GNUNET_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 */
421void
422GNUNET_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 */
462void
463GNUNET_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
483int
484GNUNET_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 */
57int
58GNUNET_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 */
76void
77GNUNET_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 */
86int
87GNUNET_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 */
96void
97GNUNET_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 */
108int
109GNUNET_FS_indexing_init (const struct GNUNET_CONFIGURATION_Handle *c,
110 struct GNUNET_DATASTORE_Handle *d);
111
112
113/**
114 * Shutdown the module.
115 */
116void
117GNUNET_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 */
41struct PendingRequestList;
42
43/**
44 * Transmission plan for a peer.
45 */
46struct PeerPlan;
47
48
49/**
50 * M:N binding of plans to pending requests.
51 * Each pending request can be in a number of plans,
52 * and each plan can have a number of pending requests.
53 * Objects of this type indicate a mapping of a plan to
54 * a particular pending request.
55 *
56 * The corresponding head and tail of the "PE" MDLL
57 * are stored in a `struct GSF_RequestPlan`. (We need
58 * to be able to lookup all pending requests corresponding
59 * to a given plan entry.)
60 *
61 * Similarly head and tail of the "PR" MDLL are stored
62 * with the `struct GSF_PendingRequest`. (We need
63 * to be able to lookup all plan entries corresponding
64 * to a given pending request.)
65 */
66struct GSF_PendingRequestPlanBijection
67{
68 /**
69 * This is a doubly-linked list.
70 */
71 struct GSF_PendingRequestPlanBijection *next_PR;
72
73 /**
74 * This is a doubly-linked list.
75 */
76 struct GSF_PendingRequestPlanBijection *prev_PR;
77
78 /**
79 * This is a doubly-linked list.
80 */
81 struct GSF_PendingRequestPlanBijection *next_PE;
82
83 /**
84 * This is a doubly-linked list.
85 */
86 struct GSF_PendingRequestPlanBijection *prev_PE;
87
88 /**
89 * Associated request plan (tells us one of the peers that
90 * we plan to forward the request to).
91 */
92 struct GSF_RequestPlan *rp;
93
94 /**
95 * Associated pending request (identifies request details
96 * and one of the origins of the request).
97 */
98 struct GSF_PendingRequest *pr;
99};
100
101
102/**
103 * Information we keep per request per peer. This is a doubly-linked
104 * list (with head and tail in the `struct GSF_PendingRequestData`)
105 * with one entry in each heap of each `struct PeerPlan`. Each
106 * entry tracks information relevant for this request and this peer.
107 */
108struct GSF_RequestPlan
109{
110 /**
111 * This is a doubly-linked list.
112 */
113 struct GSF_RequestPlan *next;
114
115 /**
116 * This is a doubly-linked list.
117 */
118 struct GSF_RequestPlan *prev;
119
120 /**
121 * Heap node associated with this request and this peer.
122 */
123 struct GNUNET_CONTAINER_HeapNode *hn;
124
125 /**
126 * The transmission plan for a peer that this request is associated with.
127 */
128 struct PeerPlan *pp;
129
130 /**
131 * Head of list of associated pending requests. This tells us
132 * which incoming requests from other peers this plan entry
133 * corresponds to.
134 */
135 struct GSF_PendingRequestPlanBijection *pe_head;
136
137 /**
138 * Tail of list of associated pending requests.
139 */
140 struct GSF_PendingRequestPlanBijection *pe_tail;
141
142 /**
143 * Earliest time we'd be happy to (re)transmit this request.
144 */
145 struct GNUNET_TIME_Absolute earliest_transmission;
146
147 /**
148 * When was the last time we transmitted this request to this peer? 0 for never.
149 */
150 struct GNUNET_TIME_Absolute last_transmission;
151
152 /**
153 * Current priority for this request for this target.
154 */
155 uint64_t priority;
156
157 /**
158 * How often did we transmit this request to this peer?
159 */
160 unsigned int transmission_counter;
161};
162
163
164/**
165 * Transmission plan for a peer.
166 */
167struct PeerPlan
168{
169 /**
170 * Heap with pending queries (`struct GSF_RequestPlan`), higher weights mean higher priority.
171 */
172 struct GNUNET_CONTAINER_Heap *priority_heap;
173
174 /**
175 * Heap with pending queries (`struct GSF_RequestPlan`), by transmission time, lowest first.
176 */
177 struct GNUNET_CONTAINER_Heap *delay_heap;
178
179 /**
180 * Map of queries to plan entries. All entries in the @e priority_heap
181 * or @e delay_heap should be in the @e plan_map. Note that it is
182 * possible for the @e plan_map to have multiple entries for the same
183 * query.
184 */
185 struct GNUNET_CONTAINER_MultiHashMap *plan_map;
186
187 /**
188 * Peer for which this is the plan.
189 */
190 struct GSF_ConnectedPeer *cp;
191
192 /**
193 * Current task for executing the plan.
194 */
195 struct GNUNET_SCHEDULER_Task *task;
196
197 /**
198 * Current message under transmission for the plan.
199 */
200 struct GNUNET_MQ_Envelope *env;
201};
202
203
204/**
205 * Hash map from peer identities to PeerPlans.
206 */
207static struct GNUNET_CONTAINER_MultiPeerMap *plans;
208
209/**
210 * Sum of all transmission counters (equals total delay for all plan entries).
211 */
212static unsigned long long total_delay;
213
214/**
215 * Number of plan entries.
216 */
217static unsigned long long plan_count;
218
219
220/**
221 * Return the query (key in the plan_map) for the given request plan.
222 * Note that this key may change as there can be multiple pending
223 * requests for the same key and we just return _one_ of them; this
224 * particular one might complete while another one might still be
225 * active, hence the lifetime of the returned hash code is NOT
226 * necessarily identical to that of the `struct GSF_RequestPlan`
227 * given.
228 *
229 * @param rp a request plan
230 * @return the associated query
231 */
232static const struct GNUNET_HashCode *
233get_rp_key (struct GSF_RequestPlan *rp)
234{
235 return &GSF_pending_request_get_data_ (rp->pe_head->pr)->query;
236}
237
238
239/**
240 * Insert the given request plan into the heap with the appropriate weight.
241 *
242 * @param pp associated peer's plan
243 * @param rp request to plan
244 */
245static void
246plan (struct PeerPlan *pp,
247 struct GSF_RequestPlan *rp)
248{
249#define N ((double) 128.0)
250 /**
251 * Running average delay we currently impose.
252 */
253 static double avg_delay;
254
255 struct GSF_PendingRequestData *prd;
256 struct GNUNET_TIME_Relative delay;
257
258 GNUNET_assert (rp->pp == pp);
259 GNUNET_STATISTICS_set (GSF_stats,
260 gettext_noop ("# average retransmission delay (ms)"),
261 total_delay * 1000LL / plan_count, GNUNET_NO);
262 prd = GSF_pending_request_get_data_ (rp->pe_head->pr);
263
264 if (rp->transmission_counter < 8)
265 delay =
266 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
267 rp->transmission_counter);
268 else if (rp->transmission_counter < 32)
269 delay =
270 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
271 8
272 + (1LL << (rp->transmission_counter - 8)));
273 else
274 delay =
275 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
276 8 + (1LL << 24));
277 delay.rel_value_us =
278 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
279 delay.rel_value_us + 1);
280 /* Add 0.01 to avg_delay to avoid division-by-zero later */
281 avg_delay = (((avg_delay * (N - 1.0)) + delay.rel_value_us) / N) + 0.01;
282
283 /*
284 * For the priority, we need to consider a few basic rules:
285 * 1) if we just started requesting (delay is small), we should
286 * virtually always have a priority of zero.
287 * 2) for requests with average latency, our priority should match
288 * the average priority observed on the network
289 * 3) even the longest-running requests should not be WAY out of
290 * the observed average (thus we bound by a factor of 2)
291 * 4) we add +1 to the observed average priority to avoid everyone
292 * staying put at zero (2 * 0 = 0...).
293 *
294 * Using the specific calculation below, we get:
295 *
296 * delay = 0 => priority = 0;
297 * delay = avg delay => priority = running-average-observed-priority;
298 * delay >> avg_delay => priority = 2 * running-average-observed-priority;
299 *
300 * which satisfies all of the rules above.
301 *
302 * Note: M_PI_4 = PI/4 = arctan(1)
303 */rp->priority =
304 round ((GSF_current_priorities
305 + 1.0) * atan (delay.rel_value_us / avg_delay)) / M_PI_4;
306 /* Note: usage of 'round' and 'atan' requires -lm */
307
308 if (rp->transmission_counter != 0)
309 delay.rel_value_us += TTL_DECREMENT * 1000;
310 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
311 "Considering (re)transmission number %u in %s\n",
312 (unsigned int) rp->transmission_counter,
313 GNUNET_STRINGS_relative_time_to_string (delay,
314 GNUNET_YES));
315 rp->earliest_transmission = GNUNET_TIME_relative_to_absolute (delay);
316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317 "Earliest (re)transmission for `%s' in %us\n",
318 GNUNET_h2s (&prd->query),
319 rp->transmission_counter);
320 GNUNET_assert (rp->hn == NULL);
321 if (0 == GNUNET_TIME_absolute_get_remaining (
322 rp->earliest_transmission).rel_value_us)
323 rp->hn = GNUNET_CONTAINER_heap_insert (pp->priority_heap,
324 rp,
325 rp->priority);
326 else
327 rp->hn =
328 GNUNET_CONTAINER_heap_insert (pp->delay_heap,
329 rp,
330 rp->earliest_transmission.abs_value_us);
331 GNUNET_assert (GNUNET_YES ==
332 GNUNET_CONTAINER_multihashmap_contains_value (pp->plan_map,
333 get_rp_key (rp),
334 rp));
335#undef N
336}
337
338
339/**
340 * Get the pending request with the highest TTL from the given plan.
341 *
342 * @param rp plan to investigate
343 * @return pending request with highest TTL
344 */
345struct GSF_PendingRequest *
346get_latest (const struct GSF_RequestPlan *rp)
347{
348 struct GSF_PendingRequest *ret;
349 struct GSF_PendingRequestPlanBijection *bi;
350 const struct GSF_PendingRequestData *rprd;
351 const struct GSF_PendingRequestData *prd;
352
353 bi = rp->pe_head;
354 if (NULL == bi)
355 return NULL; /* should never happen */
356 ret = bi->pr;
357 rprd = GSF_pending_request_get_data_ (ret);
358 for (bi = bi->next_PE; NULL != bi; bi = bi->next_PE)
359 {
360 GNUNET_break (GNUNET_YES ==
361 GSF_pending_request_test_active_ (bi->pr));
362 prd = GSF_pending_request_get_data_ (bi->pr);
363 if (prd->ttl.abs_value_us > rprd->ttl.abs_value_us)
364 {
365 ret = bi->pr;
366 rprd = prd;
367 }
368 }
369 return ret;
370}
371
372
373/**
374 * Figure out when and how to transmit to the given peer.
375 *
376 * @param cls the `struct PeerPlan`
377 */
378static void
379schedule_peer_transmission (void *cls)
380{
381 struct PeerPlan *pp = cls;
382 struct GSF_RequestPlan *rp;
383 struct GNUNET_TIME_Relative delay;
384
385 if (NULL != pp->task)
386 {
387 pp->task = NULL;
388 }
389 else
390 {
391 GNUNET_assert (NULL != pp->env);
392 pp->env = NULL;
393 }
394 /* move ready requests to priority queue */
395 while ((NULL != (rp = GNUNET_CONTAINER_heap_peek (pp->delay_heap))) &&
396 (0 == GNUNET_TIME_absolute_get_remaining
397 (rp->earliest_transmission).rel_value_us))
398 {
399 GNUNET_assert (rp == GNUNET_CONTAINER_heap_remove_root (pp->delay_heap));
400 rp->hn = GNUNET_CONTAINER_heap_insert (pp->priority_heap,
401 rp,
402 rp->priority);
403 }
404 if (0 == GNUNET_CONTAINER_heap_get_size (pp->priority_heap))
405 {
406 /* priority heap (still) empty, check for delay... */
407 rp = GNUNET_CONTAINER_heap_peek (pp->delay_heap);
408 if (NULL == rp)
409 {
410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411 "No active requests for plan %p.\n",
412 pp);
413 return; /* both queues empty */
414 }
415 delay = GNUNET_TIME_absolute_get_remaining (rp->earliest_transmission);
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 "Sleeping for %s before retrying requests on plan %p.\n",
418 GNUNET_STRINGS_relative_time_to_string (delay,
419 GNUNET_YES),
420 pp);
421 GNUNET_STATISTICS_set (GSF_stats,
422 gettext_noop ("# delay heap timeout (ms)"),
423 delay.rel_value_us / 1000LL, GNUNET_NO);
424
425 pp->task
426 = GNUNET_SCHEDULER_add_at (rp->earliest_transmission,
427 &schedule_peer_transmission,
428 pp);
429 return;
430 }
431#if INSANE_STATISTICS
432 GNUNET_STATISTICS_update (GSF_stats,
433 gettext_noop ("# query plans executed"),
434 1,
435 GNUNET_NO);
436#endif
437 /* process from priority heap */
438 rp = GNUNET_CONTAINER_heap_remove_root (pp->priority_heap);
439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440 "Executing query plan %p\n",
441 rp);
442 GNUNET_assert (NULL != rp);
443 rp->hn = NULL;
444 rp->last_transmission = GNUNET_TIME_absolute_get ();
445 rp->transmission_counter++;
446 total_delay++;
447 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448 "Executing plan %p executed %u times, planning retransmission\n",
449 rp,
450 rp->transmission_counter);
451 GNUNET_assert (NULL == pp->env);
452 pp->env = GSF_pending_request_get_message_ (get_latest (rp));
453 GNUNET_MQ_notify_sent (pp->env,
454 &schedule_peer_transmission,
455 pp);
456 GSF_peer_transmit_ (pp->cp,
457 GNUNET_YES,
458 rp->priority,
459 pp->env);
460 GNUNET_STATISTICS_update (GSF_stats,
461 gettext_noop (
462 "# query messages sent to other peers"),
463 1,
464 GNUNET_NO);
465 plan (pp,
466 rp);
467}
468
469
470/**
471 * Closure for merge_pr().
472 */
473struct MergeContext
474{
475 /**
476 * Request we are trying to merge.
477 */
478 struct GSF_PendingRequest *pr;
479
480 /**
481 * Set to #GNUNET_YES if we succeeded to merge.
482 */
483 int merged;
484};
485
486
487/**
488 * Iterator that checks if an equivalent request is already
489 * present for this peer.
490 *
491 * @param cls closure
492 * @param query the query
493 * @param element request plan stored at the node
494 * @return #GNUNET_YES if we should continue to iterate,
495 * #GNUNET_NO if not (merge success)
496 */
497static int
498merge_pr (void *cls,
499 const struct GNUNET_HashCode *query,
500 void *element)
501{
502 struct MergeContext *mpr = cls;
503 struct GSF_RequestPlan *rp = element;
504 struct GSF_PendingRequestData *prd;
505 struct GSF_PendingRequestPlanBijection *bi;
506 struct GSF_PendingRequest *latest;
507
508 GNUNET_break (GNUNET_YES ==
509 GSF_pending_request_test_active_ (mpr->pr));
510 if (GNUNET_OK !=
511 GSF_pending_request_is_compatible_ (mpr->pr,
512 rp->pe_head->pr))
513 return GNUNET_YES;
514 /* merge new request with existing request plan */
515 bi = GNUNET_new (struct GSF_PendingRequestPlanBijection);
516 bi->rp = rp;
517 bi->pr = mpr->pr;
518 prd = GSF_pending_request_get_data_ (mpr->pr);
519 GNUNET_CONTAINER_MDLL_insert (PR,
520 prd->pr_head,
521 prd->pr_tail,
522 bi);
523 GNUNET_CONTAINER_MDLL_insert (PE,
524 rp->pe_head,
525 rp->pe_tail,
526 bi);
527 mpr->merged = GNUNET_YES;
528#if INSANE_STATISTICS
529 GNUNET_STATISTICS_update (GSF_stats,
530 gettext_noop ("# requests merged"),
531 1,
532 GNUNET_NO);
533#endif
534 latest = get_latest (rp);
535 if (GSF_pending_request_get_data_ (latest)->ttl.abs_value_us <
536 prd->ttl.abs_value_us)
537 {
538#if INSANE_STATISTICS
539 GNUNET_STATISTICS_update (GSF_stats,
540 gettext_noop ("# requests refreshed"),
541 1,
542 GNUNET_NO);
543#endif
544 rp->transmission_counter = 0; /* reset */
545 }
546 return GNUNET_NO;
547}
548
549
550/**
551 * Create a new query plan entry.
552 *
553 * @param cp peer with the entry
554 * @param pr request with the entry
555 */
556void
557GSF_plan_add_ (struct GSF_ConnectedPeer *cp,
558 struct GSF_PendingRequest *pr)
559{
560 const struct GNUNET_PeerIdentity *id;
561 struct PeerPlan *pp;
562 struct GSF_PendingRequestData *prd;
563 struct GSF_RequestPlan *rp;
564 struct GSF_PendingRequestPlanBijection *bi;
565 struct MergeContext mpc;
566
567 GNUNET_assert (GNUNET_YES ==
568 GSF_pending_request_test_active_ (pr));
569 GNUNET_assert (NULL != cp);
570 id = GSF_connected_peer_get_identity2_ (cp);
571 pp = GNUNET_CONTAINER_multipeermap_get (plans, id);
572 if (NULL == pp)
573 {
574 pp = GNUNET_new (struct PeerPlan);
575 pp->plan_map = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
576 pp->priority_heap =
577 GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
578 pp->delay_heap =
579 GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
580 pp->cp = cp;
581 GNUNET_assert (GNUNET_OK ==
582 GNUNET_CONTAINER_multipeermap_put (plans,
583 id,
584 pp,
585 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
586 pp->task = GNUNET_SCHEDULER_add_now (&schedule_peer_transmission,
587 pp);
588 }
589 mpc.merged = GNUNET_NO;
590 mpc.pr = pr;
591 prd = GSF_pending_request_get_data_ (pr);
592 GNUNET_CONTAINER_multihashmap_get_multiple (pp->plan_map,
593 &prd->query,
594 &merge_pr,
595 &mpc);
596 if (GNUNET_NO != mpc.merged)
597 return;
598 plan_count++;
599 GNUNET_STATISTICS_update (GSF_stats,
600 gettext_noop ("# query plan entries"),
601 1,
602 GNUNET_NO);
603 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
604 "Planning transmission of query `%s' to peer `%s'\n",
605 GNUNET_h2s (&prd->query),
606 GNUNET_i2s (id));
607 rp = GNUNET_new (struct GSF_RequestPlan);
608 bi = GNUNET_new (struct GSF_PendingRequestPlanBijection);
609 bi->rp = rp;
610 bi->pr = pr;
611 GNUNET_CONTAINER_MDLL_insert (PR,
612 prd->pr_head,
613 prd->pr_tail,
614 bi);
615 GNUNET_CONTAINER_MDLL_insert (PE,
616 rp->pe_head,
617 rp->pe_tail,
618 bi);
619 rp->pp = pp;
620 GNUNET_assert (GNUNET_YES ==
621 GNUNET_CONTAINER_multihashmap_put (pp->plan_map,
622 get_rp_key (rp),
623 rp,
624 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
625 plan (pp,
626 rp);
627}
628
629
630/**
631 * Notify the plan about a peer being no longer available;
632 * destroy all entries associated with this peer.
633 *
634 * @param cp connected peer
635 */
636void
637GSF_plan_notify_peer_disconnect_ (const struct GSF_ConnectedPeer *cp)
638{
639 const struct GNUNET_PeerIdentity *id;
640 struct PeerPlan *pp;
641 struct GSF_RequestPlan *rp;
642 struct GSF_PendingRequestData *prd;
643 struct GSF_PendingRequestPlanBijection *bi;
644
645 id = GSF_connected_peer_get_identity2_ (cp);
646 pp = GNUNET_CONTAINER_multipeermap_get (plans, id);
647 if (NULL == pp)
648 return; /* nothing was ever planned for this peer */
649 GNUNET_assert (GNUNET_YES ==
650 GNUNET_CONTAINER_multipeermap_remove (plans, id,
651 pp));
652 if (NULL != pp->task)
653 {
654 GNUNET_SCHEDULER_cancel (pp->task);
655 pp->task = NULL;
656 }
657 while (NULL != (rp = GNUNET_CONTAINER_heap_remove_root (pp->priority_heap)))
658 {
659 GNUNET_break (GNUNET_YES ==
660 GNUNET_CONTAINER_multihashmap_remove (pp->plan_map,
661 get_rp_key (rp),
662 rp));
663 while (NULL != (bi = rp->pe_head))
664 {
665 GNUNET_CONTAINER_MDLL_remove (PE,
666 rp->pe_head,
667 rp->pe_tail,
668 bi);
669 prd = GSF_pending_request_get_data_ (bi->pr);
670 GNUNET_CONTAINER_MDLL_remove (PR,
671 prd->pr_head,
672 prd->pr_tail,
673 bi);
674 GNUNET_free (bi);
675 }
676 plan_count--;
677 GNUNET_free (rp);
678 }
679 GNUNET_CONTAINER_heap_destroy (pp->priority_heap);
680 while (NULL != (rp = GNUNET_CONTAINER_heap_remove_root (pp->delay_heap)))
681 {
682 GNUNET_break (GNUNET_YES ==
683 GNUNET_CONTAINER_multihashmap_remove (pp->plan_map,
684 get_rp_key (rp),
685 rp));
686 while (NULL != (bi = rp->pe_head))
687 {
688 prd = GSF_pending_request_get_data_ (bi->pr);
689 GNUNET_CONTAINER_MDLL_remove (PE,
690 rp->pe_head,
691 rp->pe_tail,
692 bi);
693 GNUNET_CONTAINER_MDLL_remove (PR,
694 prd->pr_head,
695 prd->pr_tail,
696 bi);
697 GNUNET_free (bi);
698 }
699 plan_count--;
700 GNUNET_free (rp);
701 }
702 GNUNET_STATISTICS_set (GSF_stats,
703 gettext_noop ("# query plan entries"),
704 plan_count,
705 GNUNET_NO);
706 GNUNET_CONTAINER_heap_destroy (pp->delay_heap);
707 GNUNET_CONTAINER_multihashmap_destroy (pp->plan_map);
708 GNUNET_free (pp);
709}
710
711
712/**
713 * Get the last transmission attempt time for the request plan list
714 * referenced by @a pr_head, that was sent to @a sender
715 *
716 * @param pr_head request plan reference list to check.
717 * @param sender the peer that we've sent the request to.
718 * @param result the timestamp to fill, set to #GNUNET_TIME_UNIT_FOREVER_ABS if never transmitted
719 * @return #GNUNET_YES if @a result was changed, #GNUNET_NO otherwise.
720 */
721int
722GSF_request_plan_reference_get_last_transmission_ (struct
723 GSF_PendingRequestPlanBijection
724 *pr_head,
725 struct GSF_ConnectedPeer *
726 sender,
727 struct GNUNET_TIME_Absolute *
728 result)
729{
730 struct GSF_PendingRequestPlanBijection *bi;
731
732 for (bi = pr_head; NULL != bi; bi = bi->next_PR)
733 {
734 if (bi->rp->pp->cp == sender)
735 {
736 if (0 == bi->rp->last_transmission.abs_value_us)
737 *result = GNUNET_TIME_UNIT_FOREVER_ABS;
738 else
739 *result = bi->rp->last_transmission;
740 return GNUNET_YES;
741 }
742 }
743 return GNUNET_NO;
744}
745
746
747/**
748 * Notify the plan about a request being done; destroy all entries
749 * associated with this request.
750 *
751 * @param pr request that is done
752 */
753void
754GSF_plan_notify_request_done_ (struct GSF_PendingRequest *pr)
755{
756 struct GSF_RequestPlan *rp;
757 struct GSF_PendingRequestData *prd;
758 struct GSF_PendingRequestPlanBijection *bi;
759
760 prd = GSF_pending_request_get_data_ (pr);
761 while (NULL != (bi = prd->pr_head))
762 {
763 rp = bi->rp;
764 GNUNET_CONTAINER_MDLL_remove (PR,
765 prd->pr_head,
766 prd->pr_tail,
767 bi);
768 GNUNET_CONTAINER_MDLL_remove (PE,
769 rp->pe_head,
770 rp->pe_tail,
771 bi);
772 GNUNET_assert (bi->pr == pr);
773 if (NULL == rp->pe_head)
774 {
775 GNUNET_CONTAINER_heap_remove_node (rp->hn);
776 plan_count--;
777 GNUNET_break (GNUNET_YES ==
778 GNUNET_CONTAINER_multihashmap_remove (rp->pp->plan_map,
779 &prd->query,
780 rp));
781 GNUNET_free (rp);
782 }
783 GNUNET_free (bi);
784 }
785 GNUNET_STATISTICS_set (GSF_stats,
786 gettext_noop ("# query plan entries"),
787 plan_count,
788 GNUNET_NO);
789}
790
791
792/**
793 * Initialize plan subsystem.
794 */
795void
796GSF_plan_init ()
797{
798 plans = GNUNET_CONTAINER_multipeermap_create (256,
799 GNUNET_YES);
800}
801
802
803/**
804 * Shutdown plan subsystem.
805 */
806void
807GSF_plan_done ()
808{
809 GNUNET_assert (0 == GNUNET_CONTAINER_multipeermap_size (plans));
810 GNUNET_CONTAINER_multipeermap_destroy (plans);
811}
812
813
814/* end of gnunet-service-fs_pe.h */
diff --git a/src/fs/gnunet-service-fs_pe.h b/src/fs/gnunet-service-fs_pe.h
deleted file mode 100644
index d532b6b71..000000000
--- a/src/fs/gnunet-service-fs_pe.h
+++ /dev/null
@@ -1,95 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_pe.h
23 * @brief API to manage query plan
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_FS_PE_H
27#define GNUNET_SERVICE_FS_PE_H
28
29#include "gnunet-service-fs.h"
30
31
32/**
33 * Create a new query plan entry.
34 *
35 * @param cp peer with the entry
36 * @param pr request with the entry
37 */
38void
39GSF_plan_add_ (struct GSF_ConnectedPeer *cp,
40 struct GSF_PendingRequest *pr);
41
42
43/**
44 * Notify the plan about a peer being no longer available;
45 * destroy all entries associated with this peer.
46 *
47 * @param cp connected peer
48 */
49void
50GSF_plan_notify_peer_disconnect_ (const struct GSF_ConnectedPeer *cp);
51
52
53/**
54 * Notify the plan about a request being done;
55 * destroy all entries associated with this request.
56 *
57 * @param pr request that is done
58 */
59void
60GSF_plan_notify_request_done_ (struct GSF_PendingRequest *pr);
61
62/**
63 * Get the last transmission attempt time for the request plan list
64 * referenced by 'rpr_head', that was sent to 'sender'
65 *
66 * @param pr_head request plan reference list to check.
67 * @param sender the peer that we've sent the request to.
68 * @param result the timestamp to fill.
69 * @return GNUNET_YES if 'result' was changed, GNUNET_NO otherwise.
70 */
71int
72GSF_request_plan_reference_get_last_transmission_ (struct
73 GSF_PendingRequestPlanBijection
74 *pr_head,
75 struct GSF_ConnectedPeer *
76 sender,
77 struct GNUNET_TIME_Absolute *
78 result);
79
80/**
81 * Initialize plan subsystem.
82 */
83void
84GSF_plan_init (void);
85
86
87/**
88 * Shutdown plan subsystem.
89 */
90void
91GSF_plan_done (void);
92
93
94#endif
95/* end of gnunet-service-fs_pe.h */
diff --git a/src/fs/gnunet-service-fs_pr.c b/src/fs/gnunet-service-fs_pr.c
deleted file mode 100644
index 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 */
77struct GSF_PendingRequest
78{
79 /**
80 * Public data for the request.
81 */
82 struct GSF_PendingRequestData public_data;
83
84 /**
85 * Function to call if we encounter a reply.
86 */
87 GSF_PendingRequestReplyHandler rh;
88
89 /**
90 * Closure for @e rh
91 */
92 void *rh_cls;
93
94 /**
95 * Array of hash codes of replies we've already seen.
96 */
97 struct GNUNET_HashCode *replies_seen;
98
99 /**
100 * Block group for filtering replies we've already seen.
101 */
102 struct GNUNET_BLOCK_Group *bg;
103
104 /**
105 * Entry for this pending request in the expiration heap, or NULL.
106 */
107 struct GNUNET_CONTAINER_HeapNode *hnode;
108
109 /**
110 * Datastore queue entry for this request (or NULL for none).
111 */
112 struct GNUNET_DATASTORE_QueueEntry *qe;
113
114 /**
115 * DHT request handle for this request (or NULL for none).
116 */
117 struct GNUNET_DHT_GetHandle *gh;
118
119 /**
120 * Cadet request handle for this request (or NULL for none).
121 */
122 struct GSF_CadetRequest *cadet_request;
123
124 /**
125 * Function to call upon completion of the local get
126 * request, or NULL for none.
127 */
128 GSF_LocalLookupContinuation llc_cont;
129
130 /**
131 * Closure for @e llc_cont.
132 */
133 void *llc_cont_cls;
134
135 /**
136 * Last result from the local datastore lookup evaluation.
137 */
138 enum GNUNET_BLOCK_ReplyEvaluationResult local_result;
139
140 /**
141 * Identity of the peer that we should use for the 'sender'
142 * (recipient of the response) when forwarding (0 for none).
143 */
144 GNUNET_PEER_Id sender_pid;
145
146 /**
147 * Identity of the peer that we should never forward this query
148 * to since it originated this query (0 for none).
149 */
150 GNUNET_PEER_Id origin_pid;
151
152 /**
153 * Time we started the last datastore lookup.
154 */
155 struct GNUNET_TIME_Absolute qe_start;
156
157 /**
158 * Task that warns us if the local datastore lookup takes too long.
159 */
160 struct GNUNET_SCHEDULER_Task *warn_task;
161
162 /**
163 * Do we have a first UID yet?
164 */
165 bool have_first_uid;
166
167 /**
168 * Have we seen a NULL result yet?
169 */
170 bool seen_null;
171
172 /**
173 * Unique ID of the first result from the local datastore;
174 * used to terminate the loop.
175 */
176 uint64_t first_uid;
177
178 /**
179 * Result count.
180 */
181 size_t result_count;
182
183 /**
184 * How often have we retried this request via 'cadet'?
185 * (used to bound overall retries).
186 */
187 unsigned int cadet_retry_count;
188
189 /**
190 * Number of valid entries in the 'replies_seen' array.
191 */
192 unsigned int replies_seen_count;
193
194 /**
195 * Length of the 'replies_seen' array.
196 */
197 unsigned int replies_seen_size;
198};
199
200
201/**
202 * All pending requests, ordered by the query. Entries
203 * are of type 'struct GSF_PendingRequest*'.
204 */
205static struct GNUNET_CONTAINER_MultiHashMap *pr_map;
206
207
208/**
209 * Datastore 'PUT' load tracking.
210 */
211static struct GNUNET_LOAD_Value *datastore_put_load;
212
213
214/**
215 * Are we allowed to migrate content to this peer.
216 */
217static int active_to_migration;
218
219
220/**
221 * Heap with the request that will expire next at the top. Contains
222 * pointers of type "struct PendingRequest*"; these will *also* be
223 * aliased from the "requests_by_peer" data structures and the
224 * "requests_by_query" table. Note that requests from our clients
225 * don't expire and are thus NOT in the "requests_by_expiration"
226 * (or the "requests_by_peer" tables).
227 */
228static struct GNUNET_CONTAINER_Heap *requests_by_expiration_heap;
229
230
231/**
232 * Maximum number of requests (from other peers, overall) that we're
233 * willing to have pending at any given point in time. Can be changed
234 * via the configuration file (32k is just the default).
235 */
236static unsigned long long max_pending_requests = (32 * 1024);
237
238
239/**
240 * Recalculate our bloom filter for filtering replies. This function
241 * will create a new bloom filter from scratch, so it should only be
242 * called if we have no bloomfilter at all (and hence can create a
243 * fresh one of minimal size without problems) OR if our peer is the
244 * initiator (in which case we may resize to larger than minimum size).
245 *
246 * @param type type of the request
247 * @param pr request for which the BF is to be recomputed
248 */
249static void
250refresh_bloomfilter (enum GNUNET_BLOCK_Type type,
251 struct GSF_PendingRequest *pr)
252{
253 if (NULL != pr->bg)
254 {
255 GNUNET_BLOCK_group_destroy (pr->bg);
256 pr->bg = NULL;
257 }
258 if (GNUNET_BLOCK_TYPE_FS_UBLOCK != type)
259 return; /* no need */
260 pr->bg =
261 GNUNET_BLOCK_group_create (GSF_block_ctx,
262 type,
263 NULL,
264 0,
265 "seen-set-size",
266 pr->replies_seen_count,
267 NULL);
268 if (NULL == pr->bg)
269 return;
270 GNUNET_break (GNUNET_OK ==
271 GNUNET_BLOCK_group_set_seen (pr->bg,
272 pr->replies_seen,
273 pr->replies_seen_count));
274}
275
276
277struct GSF_PendingRequest *
278GSF_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 */
409struct GSF_PendingRequestData *
410GSF_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 */
425int
426GSF_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
438void
439GSF_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 */
488struct GNUNET_MQ_Envelope *
489GSF_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 */
572static int
573clean_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 */
640void
641GSF_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
689void
690GSF_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 */
702struct 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 */
758static void
759update_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 */
778static enum GNUNET_GenericReturnValue
779process_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 */
904struct 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 */
933static void
934put_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 */
1018static int
1019test_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 */
1055static void
1056handle_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 */
1127void
1128GSF_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 */
1176static void
1177cadet_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 */
1251void
1252GSF_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 */
1277static void
1278warn_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 */
1298static void
1299odc_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) */
1315static void
1316call_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 */
1361static void
1362no_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 */
1380static void
1381process_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 */
1394static void
1395start_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 */
1458static void
1459process_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 }
1577called_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 */
1646int
1647GSF_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 */
1668void
1669GSF_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 */
1697void
1698handle_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 */
1839int
1840GSF_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 */
1849void
1850GSF_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 */
1874void
1875GSF_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 */
35enum GSF_PendingRequestOptions
36{
37 /**
38 * No special options (P2P-default).
39 */
40 GSF_PRO_DEFAULTS = 0,
41
42 /**
43 * Request must only be processed locally.
44 */
45 GSF_PRO_LOCAL_ONLY = 1,
46
47 /**
48 * Request must only be forwarded (no routing)
49 */
50 GSF_PRO_FORWARD_ONLY = 2,
51
52 /**
53 * Request persists indefinitely (no expiration).
54 */
55 GSF_PRO_REQUEST_NEVER_EXPIRES = 4,
56
57 /**
58 * Request is allowed to refresh bloomfilter and change mingle value.
59 */
60 GSF_PRO_BLOOMFILTER_FULL_REFRESH = 8,
61
62 /**
63 * Request priority is allowed to be exceeded.
64 */
65 GSF_PRO_PRIORITY_UNLIMITED = 16,
66
67 /**
68 * Option mask for typical local requests.
69 */
70 GSF_PRO_LOCAL_REQUEST =
71 (GSF_PRO_BLOOMFILTER_FULL_REFRESH | GSF_PRO_PRIORITY_UNLIMITED
72 | GSF_PRO_REQUEST_NEVER_EXPIRES)
73};
74
75
76/**
77 * Public data (in the sense of not encapsulated within
78 * 'gnunet-service-fs_pr', not in the sense of network-wide
79 * known) associated with each pending request.
80 */
81struct GSF_PendingRequestData
82{
83 /**
84 * Primary query hash for this request.
85 */
86 struct GNUNET_HashCode query;
87
88 /**
89 * Identity of a peer hosting the content, otherwise NULl.
90 * Allocated after struct only if needed. Do not free!
91 */
92 const struct GNUNET_PeerIdentity *target;
93
94 /**
95 * Fields for the plan module to track a DLL with the request.
96 */
97 struct GSF_PendingRequestPlanBijection *pr_head;
98
99 /**
100 * Fields for the plan module to track a DLL with the request.
101 */
102 struct GSF_PendingRequestPlanBijection *pr_tail;
103
104 /**
105 * Current TTL for the request.
106 */
107 struct GNUNET_TIME_Absolute ttl;
108
109 /**
110 * When did we start with the request.
111 */
112 struct GNUNET_TIME_Absolute start_time;
113
114 /**
115 * Desired anonymity level.
116 */
117 uint32_t anonymity_level;
118
119 /**
120 * Priority that this request (still) has for us.
121 */
122 uint32_t priority;
123
124 /**
125 * Priority that this request (originally) had for us.
126 */
127 uint32_t original_priority;
128
129 /**
130 * Counter for how often this request has been transmitted (estimate,
131 * because we might have the same request pending for multiple clients,
132 * and of course because a transmission may have failed at a lower
133 * layer).
134 */
135 uint32_t num_transmissions;
136
137 /**
138 * How much respect did we (in total) offer for this request so far (estimate,
139 * because we might have the same request pending for multiple clients,
140 * and of course because a transmission may have failed at a lower
141 * layer).
142 */
143 uint32_t respect_offered;
144
145 /**
146 * Options for the request.
147 */
148 enum GSF_PendingRequestOptions options;
149
150 /**
151 * Type of the requested block.
152 */
153 enum GNUNET_BLOCK_Type type;
154
155 /**
156 * Number of results we have found for this request so far.
157 */
158 unsigned int results_found;
159
160 /**
161 * Has this request been started yet (local/p2p operations)? Or are
162 * we still constructing it?
163 */
164 int has_started;
165};
166
167
168/**
169 * Handle a reply to a pending request. Also called if a request
170 * expires (then with data == NULL). The handler may be called
171 * many times (depending on the request type), but will not be
172 * called during or after a call to GSF_pending_request_cancel
173 * and will also not be called anymore after a call signalling
174 * expiration.
175 *
176 * @param cls user-specified closure
177 * @param eval evaluation of the result
178 * @param pr handle to the original pending request
179 * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown"
180 * @param expiration when does @a data expire?
181 * @param last_transmission the last time we've tried to get this block (FOREVER if unknown)
182 * @param type type of the block
183 * @param data response data, NULL on request expiration
184 * @param data_len number of bytes in @a data
185 */
186typedef void
187(*GSF_PendingRequestReplyHandler) (
188 void *cls,
189 enum GNUNET_BLOCK_ReplyEvaluationResult eval,
190 struct GSF_PendingRequest *pr,
191 uint32_t reply_anonymity_level,
192 struct GNUNET_TIME_Absolute expiration,
193 struct GNUNET_TIME_Absolute
194 last_transmission,
195 enum GNUNET_BLOCK_Type type,
196 const void *data,
197 size_t data_len);
198
199
200/**
201 * Create a new pending request.
202 *
203 * @param options request options
204 * @param type type of the block that is being requested
205 * @param query key for the lookup
206 * @param target preferred target for the request, NULL for none
207 * @param bf_data raw data for bloom filter for known replies, can be NULL
208 * @param bf_size number of bytes in bf_data
209 * @param anonymity_level desired anonymity level
210 * @param priority maximum outgoing cumulative request priority to use
211 * @param ttl current time-to-live for the request
212 * @param sender_pid peer ID to use for the sender when forwarding, 0 for none;
213 * reference counter is taken over by this function
214 * @param origin_pid peer ID of origin of query (do not loop back)
215 * @param replies_seen hash codes of known local replies
216 * @param replies_seen_count size of the 'replies_seen' array
217 * @param rh handle to call when we get a reply
218 * @param rh_cls closure for rh
219 * @return handle for the new pending request
220 */
221struct GSF_PendingRequest *
222GSF_pending_request_create_ (enum GSF_PendingRequestOptions options,
223 enum GNUNET_BLOCK_Type type,
224 const struct GNUNET_HashCode *query,
225 const struct GNUNET_PeerIdentity *target,
226 const char *bf_data,
227 size_t bf_size,
228 uint32_t anonymity_level,
229 uint32_t priority,
230 int32_t ttl,
231 GNUNET_PEER_Id sender_pid,
232 GNUNET_PEER_Id origin_pid,
233 const struct GNUNET_HashCode *replies_seen,
234 unsigned int replies_seen_count,
235 GSF_PendingRequestReplyHandler rh,
236 void *rh_cls);
237
238
239/**
240 * Update a given pending request with additional replies
241 * that have been seen.
242 *
243 * @param pr request to update
244 * @param replies_seen hash codes of replies that we've seen
245 * @param replies_seen_count size of the @a replies_seen array
246 */
247void
248GSF_pending_request_update_ (struct GSF_PendingRequest *pr,
249 const struct GNUNET_HashCode *replies_seen,
250 unsigned int replies_seen_count);
251
252
253/**
254 * Obtain the public data associated with a pending request
255 *
256 * @param pr pending request
257 * @return associated public data
258 */
259struct GSF_PendingRequestData *
260GSF_pending_request_get_data_ (struct GSF_PendingRequest *pr);
261
262
263/**
264 * Check if the given request is still active.
265 *
266 * @param pr pending request
267 * @return #GNUNET_YES if the request is still active
268 */
269int
270GSF_pending_request_test_active_ (struct GSF_PendingRequest *pr);
271
272
273/**
274 * Test if two pending requests are compatible (would generate
275 * the same query modulo filters and should thus be processed
276 * jointly).
277 *
278 * @param pra a pending request
279 * @param prb another pending request
280 * @return #GNUNET_OK if the requests are compatible
281 */
282int
283GSF_pending_request_is_compatible_ (struct GSF_PendingRequest *pra,
284 struct GSF_PendingRequest *prb);
285
286
287/**
288 * Generate the message corresponding to the given pending request for
289 * transmission to other peers.
290 *
291 * @param pr request to generate the message for
292 * @return envelope with the request message
293 */
294struct GNUNET_MQ_Envelope *
295GSF_pending_request_get_message_ (struct GSF_PendingRequest *pr);
296
297
298/**
299 * Explicitly cancel a pending request.
300 *
301 * @param pr request to cancel
302 * @param full_cleanup fully purge the request
303 */
304void
305GSF_pending_request_cancel_ (struct GSF_PendingRequest *pr,
306 int full_cleanup);
307
308
309/**
310 * Signature of function called on each request.
311 * (Note: 'subtype' of GNUNET_CONTAINER_HashMapIterator).
312 *
313 * @param cls closure
314 * @param key query for the request
315 * @param pr handle to the pending request
316 * @return #GNUNET_YES to continue to iterate
317 */
318typedef int
319(*GSF_PendingRequestIterator) (void *cls,
320 const struct GNUNET_HashCode *key,
321 struct GSF_PendingRequest *pr);
322
323
324/**
325 * Iterate over all pending requests.
326 *
327 * @param it function to call for each request
328 * @param cls closure for it
329 */
330void
331GSF_iterate_pending_requests_ (GSF_PendingRequestIterator it,
332 void *cls);
333
334
335/**
336 * Handle P2P "CONTENT" message. Checks that the message is
337 * well-formed and then checks if there are any pending requests for
338 * this content and possibly passes it on (to local clients or other
339 * peers). Does NOT perform migration (content caching at this peer).
340 *
341 * @param cls the other peer involved (sender)
342 * @param put the actual message
343 */
344void
345handle_p2p_put (void *cls,
346 const struct PutMessage *put);
347
348
349/**
350 * Consider looking up the data in the DHT (anonymity-level permitting).
351 *
352 * @param pr the pending request to process
353 */
354void
355GSF_dht_lookup_ (struct GSF_PendingRequest *pr);
356
357
358/**
359 * Consider downloading via cadet (if possible)
360 *
361 * @param pr the pending request to process
362 */
363void
364GSF_cadet_lookup_ (struct GSF_PendingRequest *pr);
365
366
367/**
368 * Function to be called after we're done processing
369 * replies from the local lookup.
370 *
371 * @param cls closure
372 * @param pr the pending request we were processing
373 * @param result final datastore lookup result
374 */
375typedef void
376(*GSF_LocalLookupContinuation) (
377 void *cls,
378 struct GSF_PendingRequest *pr,
379 enum GNUNET_BLOCK_ReplyEvaluationResult result);
380
381
382/**
383 * Look up the request in the local datastore.
384 *
385 * @param pr the pending request to process
386 * @param cont function to call at the end
387 * @param cont_cls closure for @a cont
388 */
389void
390GSF_local_lookup_ (struct GSF_PendingRequest *pr,
391 GSF_LocalLookupContinuation cont,
392 void *cont_cls);
393
394
395/**
396 * Is the given target a legitimate peer for forwarding the given request?
397 *
398 * @param pr request
399 * @param target
400 * @return #GNUNET_YES if this request could be forwarded to the given peer
401 */
402int
403GSF_pending_request_test_target_ (struct GSF_PendingRequest *pr,
404 const struct GNUNET_PeerIdentity *target);
405
406
407/**
408 * Setup the subsystem.
409 */
410void
411GSF_pending_request_init_ (void);
412
413
414/**
415 * Shutdown the subsystem.
416 */
417void
418GSF_pending_request_done_ (void);
419
420
421#endif
422/* end of gnunet-service-fs_pr.h */
diff --git a/src/fs/gnunet-service-fs_push.c b/src/fs/gnunet-service-fs_push.c
deleted file mode 100644
index 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 */
59struct MigrationReadyBlock
60{
61 /**
62 * This is a doubly-linked list.
63 */
64 struct MigrationReadyBlock *next;
65
66 /**
67 * This is a doubly-linked list.
68 */
69 struct MigrationReadyBlock *prev;
70
71 /**
72 * Query for the block.
73 */
74 struct GNUNET_HashCode query;
75
76 /**
77 * When does this block expire?
78 */
79 struct GNUNET_TIME_Absolute expiration;
80
81 /**
82 * Peers we already forwarded this
83 * block to. Zero for empty entries.
84 */
85 GNUNET_PEER_Id target_list[MIGRATION_LIST_SIZE];
86
87 /**
88 * Size of the block.
89 */
90 size_t size;
91
92 /**
93 * Number of targets already used.
94 */
95 unsigned int used_targets;
96
97 /**
98 * Type of the block.
99 */
100 enum GNUNET_BLOCK_Type type;
101};
102
103
104/**
105 * Information about a peer waiting for migratable data.
106 */
107struct MigrationReadyPeer
108{
109 /**
110 * This is a doubly-linked list.
111 */
112 struct MigrationReadyPeer *next;
113
114 /**
115 * This is a doubly-linked list.
116 */
117 struct MigrationReadyPeer *prev;
118
119 /**
120 * Handle to peer.
121 */
122 struct GSF_ConnectedPeer *peer;
123
124 /**
125 * Envelope of the currently pushed message.
126 */
127 struct GNUNET_MQ_Envelope *env;
128};
129
130
131/**
132 * Head of linked list of blocks that can be migrated.
133 */
134static struct MigrationReadyBlock *mig_head;
135
136/**
137 * Tail of linked list of blocks that can be migrated.
138 */
139static struct MigrationReadyBlock *mig_tail;
140
141/**
142 * Head of linked list of peers.
143 */
144static struct MigrationReadyPeer *peer_head;
145
146/**
147 * Tail of linked list of peers.
148 */
149static struct MigrationReadyPeer *peer_tail;
150
151/**
152 * Request to datastore for migration (or NULL).
153 */
154static struct GNUNET_DATASTORE_QueueEntry *mig_qe;
155
156/**
157 * ID of task that collects blocks for migration.
158 */
159static struct GNUNET_SCHEDULER_Task *mig_task;
160
161/**
162 * What is the maximum frequency at which we are allowed to
163 * poll the datastore for migration content?
164 */
165static struct GNUNET_TIME_Relative min_migration_delay;
166
167/**
168 * Size of the doubly-linked list of migration blocks.
169 */
170static unsigned int mig_size;
171
172/**
173 * Is this module enabled?
174 */
175static int enabled;
176
177/**
178 * Did we find anything in the datastore?
179 */
180static int value_found;
181
182
183/**
184 * Delete the given migration block.
185 *
186 * @param mb block to delete
187 */
188static void
189delete_migration_block (struct MigrationReadyBlock *mb)
190{
191 GNUNET_CONTAINER_DLL_remove (mig_head,
192 mig_tail,
193 mb);
194 GNUNET_PEER_decrement_rcs (mb->target_list,
195 MIGRATION_LIST_SIZE);
196 mig_size--;
197 GNUNET_free (mb);
198}
199
200
201/**
202 * Find content for migration to this peer.
203 *
204 * @param cls A `struct MigrationReadyPeer *` to find content for
205 */
206static void
207find_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 */
217static int
218transmit_content (struct MigrationReadyPeer *mrp,
219 struct MigrationReadyBlock *block)
220{
221 struct PutMessage *msg;
222 unsigned int i;
223 struct GSF_PeerPerformanceData *ppd;
224 int ret;
225
226 ppd = GSF_get_peer_performance_data_ (mrp->peer);
227 GNUNET_assert (NULL == mrp->env);
228 mrp->env = GNUNET_MQ_msg_extra (msg,
229 block->size,
230 GNUNET_MESSAGE_TYPE_FS_PUT);
231 msg->type = htonl (block->type);
232 msg->expiration = GNUNET_TIME_absolute_hton (block->expiration);
233 GNUNET_memcpy (&msg[1],
234 &block[1],
235 block->size);
236 for (i = 0; i < MIGRATION_LIST_SIZE; i++)
237 {
238 if (block->target_list[i] == 0)
239 {
240 block->target_list[i] = ppd->pid;
241 GNUNET_PEER_change_rc (block->target_list[i],
242 1);
243 break;
244 }
245 }
246 if (MIGRATION_LIST_SIZE == i)
247 {
248 delete_migration_block (block);
249 ret = GNUNET_YES;
250 }
251 else
252 {
253 ret = GNUNET_NO;
254 }
255 GNUNET_MQ_notify_sent (mrp->env,
256 &find_content,
257 mrp);
258 GSF_peer_transmit_ (mrp->peer,
259 GNUNET_NO,
260 0 /* priority */,
261 mrp->env);
262 return ret;
263}
264
265
266/**
267 * Count the number of peers this block has
268 * already been forwarded to.
269 *
270 * @param block the block
271 * @return number of times block was forwarded
272 */
273static unsigned int
274count_targets (struct MigrationReadyBlock *block)
275{
276 unsigned int i;
277
278 for (i = 0; i < MIGRATION_LIST_SIZE; i++)
279 if (block->target_list[i] == 0)
280 return i;
281 return i;
282}
283
284
285/**
286 * Check if sending this block to this peer would
287 * be a good idea.
288 *
289 * @param mrp target peer
290 * @param block the block
291 * @return score (>= 0: feasible, negative: infeasible)
292 */
293static long
294score_content (struct MigrationReadyPeer *mrp,
295 struct MigrationReadyBlock *block)
296{
297 unsigned int i;
298 struct GSF_PeerPerformanceData *ppd;
299 struct GNUNET_PeerIdentity id;
300 struct GNUNET_HashCode hc;
301 uint32_t dist;
302
303 ppd = GSF_get_peer_performance_data_ (mrp->peer);
304 for (i = 0; i < MIGRATION_LIST_SIZE; i++)
305 if (block->target_list[i] == ppd->pid)
306 return -1;
307 GNUNET_assert (0 != ppd->pid);
308 GNUNET_PEER_resolve (ppd->pid,
309 &id);
310 GNUNET_CRYPTO_hash (&id,
311 sizeof(struct GNUNET_PeerIdentity),
312 &hc);
313 dist = GNUNET_CRYPTO_hash_distance_u32 (&block->query,
314 &hc);
315 /* closer distance, higher score: */
316 return UINT32_MAX - dist;
317}
318
319
320/**
321 * If the migration task is not currently running, consider
322 * (re)scheduling it with the appropriate delay.
323 */
324static void
325consider_gathering (void);
326
327
328static void
329find_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 */
393static void
394gather_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 */
401static void
402consider_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 */
452static void
453process_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 */
538static void
539gather_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 */
566void
567GSF_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 */
602void
603GSF_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 */
624void
625GSF_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 */
653void
654GSF_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 */
35void
36GSF_push_init_ (void);
37
38
39/**
40 * Shutdown the module.
41 */
42void
43GSF_push_done_ (void);
44
45
46/**
47 * A peer connected to us or we are now again allowed to push content.
48 * Start pushing content to this peer.
49 *
50 * @param peer handle for the peer that connected
51 */
52void
53GSF_push_start_ (struct GSF_ConnectedPeer *peer);
54
55
56/**
57 * A peer disconnected from us or asked us to stop pushing content for
58 * a while. Stop pushing content to this peer.
59 *
60 * @param peer handle for the peer that disconnected
61 */
62void
63GSF_push_stop_ (struct GSF_ConnectedPeer *peer);
64
65
66#endif
diff --git a/src/fs/gnunet-service-fs_put.c b/src/fs/gnunet-service-fs_put.c
deleted file mode 100644
index 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 */
46struct PutOperator
47{
48 /**
49 * Request to datastore for DHT PUTs (or NULL).
50 */
51 struct GNUNET_DATASTORE_QueueEntry *dht_qe;
52
53 /**
54 * Type we request from the datastore.
55 */
56 enum GNUNET_BLOCK_Type dht_put_type;
57
58 /**
59 * Handle to PUT operation.
60 */
61 struct GNUNET_DHT_PutHandle *dht_put;
62
63 /**
64 * ID of task that collects blocks for DHT PUTs.
65 */
66 struct GNUNET_SCHEDULER_Task *dht_task;
67
68 /**
69 * How many entries with zero anonymity of our type do we currently
70 * estimate to have in the database?
71 */
72 uint64_t zero_anonymity_count_estimate;
73
74 /**
75 * Count of results received from the database.
76 */
77 uint64_t result_count;
78
79 /**
80 * Next UID to request when iterating the database.
81 */
82 uint64_t next_uid;
83};
84
85
86/**
87 * ANY-terminated list of our operators (one per type
88 * of block that we're putting into the DHT).
89 */
90static struct PutOperator operators[] = {
91 { NULL, GNUNET_BLOCK_TYPE_FS_UBLOCK, 0, 0, 0 },
92 { NULL, GNUNET_BLOCK_TYPE_ANY, 0, 0, 0 }
93};
94
95
96/**
97 * Task that is run periodically to obtain blocks for DHT PUTs.
98 *
99 * @param cls type of blocks to gather
100 */
101static void
102gather_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 */
110static void
111schedule_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 */
138static void
139delay_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 */
153static void
154delay_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 */
178static void
179process_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
221static void
222gather_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 */
243void
244GSF_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 */
261void
262GSF_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 */
35void
36GSF_put_init_ (void);
37
38
39/**
40 * Shutdown the module.
41 */
42void
43GSF_put_done_ (void);
44
45
46#endif
diff --git a/src/fs/gnunet-unindex.c b/src/fs/gnunet-unindex.c
deleted file mode 100644
index 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
32static int ret;
33
34static unsigned int verbose;
35
36static const struct GNUNET_CONFIGURATION_Handle *cfg;
37
38static struct GNUNET_FS_Handle *ctx;
39
40static struct GNUNET_FS_UnindexContext *uc;
41
42
43static void
44cleanup_task (void *cls)
45{
46 GNUNET_FS_stop (ctx);
47 ctx = NULL;
48}
49
50
51static void
52shutdown_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 */
78static void *
79progress_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 */
133static void
134run (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 */
177int
178main (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 @@
1libgnunetfs_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
20gnunetservicefs_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
30configure_file(input : 'fs.conf.in',
31 output : 'fs.conf',
32 configuration : cdata,
33 install: true,
34 install_dir: pkgcfgdir)
35
36
37if get_option('monolith')
38 foreach p : libgnunetfs_src + gnunetservicefs_src
39 gnunet_src += 'fs/' + p
40 endforeach
41 subdir_done()
42endif
43
44libgnunetfs = 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'))
55libgnunetfs_dep = declare_dependency(link_with : libgnunetfs)
56pkg.generate(libgnunetfs, url: 'https://www.gnunet.org',
57 description : 'Provides API for GNUnet File-Sharing service')
58
59shared_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
67executable ('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'))
74executable ('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'))
81executable ('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'))
88executable ('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'))
95executable ('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'))
102executable ('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'))
109executable ('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'))
117executable ('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')
133executable ('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 */
46struct 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 */
92struct 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 */
127struct GNUNET_FS_MetaData *
128GNUNET_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 */
139static void
140meta_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 */
155static void
156invalidate_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
166void
167GNUNET_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
183void
184GNUNET_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
200int
201GNUNET_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. '&lt;zlib&gt;' 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 */
254int
255GNUNET_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. '&lt;zlib&gt;' 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 */
343static int
344merge_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
356void
357GNUNET_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
364int
365GNUNET_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
391void
392GNUNET_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 */
418int
419GNUNET_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
438char *
439GNUNET_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
456char *
457GNUNET_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 */
490size_t
491GNUNET_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 */
527struct GNUNET_FS_MetaData *
528GNUNET_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 */
560struct 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 */
594struct 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 */
637ssize_t
638GNUNET_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
858ssize_t
859GNUNET_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 */
885struct GNUNET_FS_MetaData *
886GNUNET_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 */
1050enum GNUNET_GenericReturnValue
1051GNUNET_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 */
1108enum GNUNET_GenericReturnValue
1109GNUNET_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 */
1158static int
1159read_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 */
1176struct GNUNET_BIO_ReadSpec
1177GNUNET_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 */
1201static int
1202write_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
1213struct GNUNET_BIO_WriteSpec
1214GNUNET_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
46static struct GNUNET_TESTBED_Peer *daemons[NUM_DAEMONS];
47
48static int ok;
49
50static struct GNUNET_TIME_Absolute start_time;
51
52static const char *progname;
53
54static struct GNUNET_TIME_Absolute start_time;
55
56
57/**
58 * Master context for 'stat_run'.
59 */
60struct StatMaster
61{
62 struct GNUNET_STATISTICS_Handle *stat;
63 struct GNUNET_TESTBED_Operation *op;
64 unsigned int daemon;
65 unsigned int value;
66};
67
68struct StatValues
69{
70 const char *subsystem;
71 const char *name;
72};
73
74/**
75 * Statistics we print out.
76 */
77static struct StatValues stats[] = {
78 { "fs", "# queries forwarded" },
79 { "fs", "# replies received and matched" },
80 { "fs", "# results found locally" },
81 { "fs", "# requests forwarded due to high load" },
82 { "fs", "# requests done for free (low load)" },
83 { "fs", "# requests dropped, priority insufficient" },
84 { "fs", "# requests done for a price (normal load)" },
85 { "fs", "# requests dropped by datastore (queue length limit)" },
86 { "fs", "# P2P searches received" },
87 { "fs", "# P2P searches discarded (queue length bound)" },
88 { "fs", "# replies received for local clients" },
89 { "fs", "# queries retransmitted to same target" },
90 { "core", "# bytes decrypted" },
91 { "core", "# bytes encrypted" },
92 { "core", "# discarded CORE_SEND requests" },
93 { "core", "# discarded CORE_SEND request bytes" },
94 { "core", "# discarded lower priority CORE_SEND requests" },
95 { "core", "# discarded lower priority CORE_SEND request bytes" },
96 { "transport", "# bytes received via TCP" },
97 { "transport", "# bytes transmitted via TCP" },
98 { "datacache", "# bytes stored" },
99 { NULL, NULL }
100};
101
102
103/**
104 * Callback function to process statistic values.
105 *
106 * @param cls closure
107 * @param subsystem name of subsystem that created the statistic
108 * @param name the name of the datum
109 * @param value the current value
110 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
111 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
112 */
113static int
114print_stat (void *cls, const char *subsystem, const char *name, uint64_t value,
115 int is_persistent)
116{
117 struct StatMaster *sm = cls;
118
119 fprintf (stderr,
120 "Peer %2u: %12s/%50s = %12llu\n",
121 sm->daemon,
122 subsystem,
123 name,
124 (unsigned long long) value);
125 return GNUNET_OK;
126}
127
128
129/**
130 * Function that gathers stats from all daemons.
131 */
132static void
133stat_run (void *cls,
134 struct GNUNET_TESTBED_Operation *op,
135 void *ca_result,
136 const char *emsg);
137
138
139/**
140 * Function called when GET operation on stats is done.
141 */
142static void
143get_done (void *cls, int success)
144{
145 struct StatMaster *sm = cls;
146
147 GNUNET_break (GNUNET_OK == success);
148 sm->value++;
149 stat_run (sm, sm->op, sm->stat, NULL);
150}
151
152
153/**
154 * Adapter function called to establish a connection to
155 * statistics service.
156 *
157 * @param cls closure
158 * @param cfg configuration of the peer to connect to; will be available until
159 * GNUNET_TESTBED_operation_done() is called on the operation returned
160 * from GNUNET_TESTBED_service_connect()
161 * @return service handle to return in 'op_result', NULL on error
162 */
163static void *
164statistics_connect_adapter (void *cls,
165 const struct GNUNET_CONFIGURATION_Handle *cfg)
166{
167 return GNUNET_STATISTICS_create ("<driver>",
168 cfg);
169}
170
171
172/**
173 * Adapter function called to destroy a connection to
174 * statistics service.
175 *
176 * @param cls closure
177 * @param op_result service handle returned from the connect adapter
178 */
179static void
180statistics_disconnect_adapter (void *cls,
181 void *op_result)
182{
183 GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
184}
185
186
187/**
188 * Function that gathers stats from all daemons.
189 */
190static void
191stat_run (void *cls,
192 struct GNUNET_TESTBED_Operation *op,
193 void *ca_result,
194 const char *emsg)
195{
196 struct StatMaster *sm = cls;
197
198 if (NULL != emsg)
199 {
200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
201 "Failed to statistics service: %s\n",
202 emsg);
203 GNUNET_SCHEDULER_shutdown ();
204 return;
205 }
206 sm->stat = ca_result;
207
208 if (stats[sm->value].name != NULL)
209 {
210 GNUNET_STATISTICS_get (sm->stat,
211#if 0
212 NULL, NULL,
213#else
214 stats[sm->value].subsystem, stats[sm->value].name,
215#endif
216 &get_done, &print_stat,
217 sm);
218 return;
219 }
220 GNUNET_TESTBED_operation_done (sm->op);
221 sm->value = 0;
222 sm->daemon++;
223 if (NUM_DAEMONS == sm->daemon)
224 {
225 GNUNET_free (sm);
226 GNUNET_SCHEDULER_shutdown ();
227 return;
228 }
229 sm->op =
230 GNUNET_TESTBED_service_connect (NULL,
231 daemons[sm->daemon],
232 "statistics",
233 &stat_run, sm,
234 &statistics_connect_adapter,
235 &statistics_disconnect_adapter,
236 NULL);
237}
238
239
240static void
241do_report (void *cls)
242{
243 char *fn = cls;
244 struct GNUNET_TIME_Relative del;
245 char *fancy;
246 struct StatMaster *sm;
247
248 if (NULL != fn)
249 {
250 GNUNET_DISK_directory_remove (fn);
251 GNUNET_free (fn);
252 }
253 if (0 ==
254 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
255 TIMEOUT)).
256 rel_value_us)
257 {
258 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
259 "Timeout during download, shutting down with error\n");
260 ok = 1;
261 GNUNET_SCHEDULER_shutdown ();
262 return;
263 }
264
265 del = GNUNET_TIME_absolute_get_duration (start_time);
266 if (del.rel_value_us == 0)
267 del.rel_value_us = 1;
268 fancy =
269 GNUNET_STRINGS_byte_size_fancy (((unsigned long long) FILESIZE)
270 * 1000000LL / del.rel_value_us);
271 fprintf (stdout,
272 "Download speed was %s/s\n",
273 fancy);
274 GNUNET_free (fancy);
275 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
276 "Finished download, shutting down\n");
277 sm = GNUNET_new (struct StatMaster);
278 sm->op =
279 GNUNET_TESTBED_service_connect (NULL,
280 daemons[sm->daemon],
281 "statistics",
282 &stat_run, sm,
283 &statistics_connect_adapter,
284 &statistics_disconnect_adapter,
285 NULL);
286}
287
288
289static void
290do_download (void *cls,
291 const struct GNUNET_FS_Uri *uri,
292 const char *fn)
293{
294 int anonymity;
295
296 if (NULL == uri)
297 {
298 GNUNET_SCHEDULER_shutdown ();
299 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
300 "Timeout during upload attempt, shutting down with error\n");
301 ok = 1;
302 return;
303 }
304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Downloading %llu bytes\n",
305 (unsigned long long) FILESIZE);
306 start_time = GNUNET_TIME_absolute_get ();
307 if (NULL != strstr (progname, "dht"))
308 anonymity = 0;
309 else
310 anonymity = 1;
311 start_time = GNUNET_TIME_absolute_get ();
312 GNUNET_FS_TEST_download (daemons[0],
313 TIMEOUT,
314 anonymity,
315 SEED,
316 uri,
317 VERBOSE,
318 &do_report,
319 (NULL == fn) ? NULL : GNUNET_strdup (fn));
320}
321
322
323static void
324do_publish (void *cls,
325 struct GNUNET_TESTBED_RunHandle *h,
326 unsigned int num_peers,
327 struct GNUNET_TESTBED_Peer **peers,
328 unsigned int links_succeeded,
329 unsigned int links_failed)
330{
331 unsigned int i;
332 int do_index;
333 int anonymity;
334
335 GNUNET_assert (NUM_DAEMONS == num_peers);
336 for (i = 0; i < num_peers; i++)
337 daemons[i] = peers[i];
338 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
339 (unsigned long long) FILESIZE);
340 if (NULL != strstr (progname, "index"))
341 do_index = GNUNET_YES;
342 else
343 do_index = GNUNET_NO;
344 if (NULL != strstr (progname, "dht"))
345 anonymity = 0;
346 else
347 anonymity = 1;
348 GNUNET_FS_TEST_publish (daemons[NUM_DAEMONS - 1], TIMEOUT, anonymity,
349 do_index, FILESIZE, SEED, VERBOSE, &do_download,
350 NULL);
351}
352
353
354int
355main (int argc, char *argv[])
356{
357 progname = argv[0];
358 (void) GNUNET_TESTBED_test_run ("perf-gnunet-service-fs-p2p",
359 "perf_gnunet_service_fs_p2p.conf",
360 NUM_DAEMONS,
361 0, NULL, NULL,
362 &do_publish, NULL);
363 GNUNET_DISK_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]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-fs-test-lib/
4
5[fs]
6GAUGER_HEAP = "2-peer 10 MB P2P download"
7# PREFIX = valgrind
diff --git a/src/fs/perf_gnunet_service_fs_p2p_respect.c b/src/fs/perf_gnunet_service_fs_p2p_respect.c
deleted file mode 100644
index 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
77static struct GNUNET_TESTBED_Peer *daemons[NUM_DAEMONS];
78
79static int ok;
80
81static struct GNUNET_TIME_Absolute start_time;
82
83static const char *progname;
84
85static struct GNUNET_FS_Uri *uri1;
86
87static struct GNUNET_FS_Uri *uri2;
88
89static char *fn1;
90
91static char *fn2;
92
93/**
94 * Master context for 'stat_run'.
95 */
96struct StatMaster
97{
98 struct GNUNET_STATISTICS_Handle *stat;
99 struct GNUNET_TESTBED_Operation *op;
100 unsigned int daemon;
101 unsigned int value;
102};
103
104struct StatValues
105{
106 const char *subsystem;
107 const char *name;
108};
109
110/**
111 * Statistics we print out.
112 */
113static struct StatValues stats[] = {
114 { "fs", "# artificial delays introduced (ms)" },
115 { "fs", "# queries forwarded" },
116 { "fs", "# replies received and matched" },
117 { "fs", "# results found locally" },
118 { "fs", "# requests forwarded due to high load" },
119 { "fs", "# requests done for free (low load)" },
120 { "fs", "# requests dropped, priority insufficient" },
121 { "fs", "# requests done for a price (normal load)" },
122 { "fs", "# requests dropped by datastore (queue length limit)" },
123 { "fs", "# P2P searches received" },
124 { "fs", "# P2P searches discarded (queue length bound)" },
125 { "fs", "# replies received for local clients" },
126 { "fs", "# queries retransmitted to same target" },
127 { "core", "# bytes decrypted" },
128 { "core", "# bytes encrypted" },
129 { "core", "# discarded CORE_SEND requests" },
130 { "core", "# discarded lower priority CORE_SEND requests" },
131 { "transport", "# bytes received via TCP" },
132 { "transport", "# bytes transmitted via TCP" },
133 { "datacache", "# bytes stored" },
134 { NULL, NULL }
135};
136
137
138static void
139cleanup ()
140{
141 GNUNET_SCHEDULER_shutdown ();
142 if (NULL != fn1)
143 {
144 GNUNET_DISK_directory_remove (fn1);
145 GNUNET_free (fn1);
146 }
147 if (NULL != fn2)
148 {
149 GNUNET_DISK_directory_remove (fn2);
150 GNUNET_free (fn2);
151 }
152}
153
154
155/**
156 * Callback function to process statistic values.
157 *
158 * @param cls closure
159 * @param subsystem name of subsystem that created the statistic
160 * @param name the name of the datum
161 * @param value the current value
162 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
163 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
164 */
165static int
166print_stat (void *cls, const char *subsystem, const char *name, uint64_t value,
167 int is_persistent)
168{
169 struct StatMaster *sm = cls;
170
171 fprintf (stderr, "Peer %2u: %12s/%50s = %12llu\n", sm->daemon, subsystem,
172 name, (unsigned long long) value);
173 return GNUNET_OK;
174}
175
176
177/**
178 * Function that gathers stats from all daemons.
179 */
180static void
181stat_run (void *cls,
182 struct GNUNET_TESTBED_Operation *op,
183 void *ca_result,
184 const char *emsg);
185
186
187/**
188 * Function called when GET operation on stats is done.
189 */
190static void
191get_done (void *cls, int success)
192{
193 struct StatMaster *sm = cls;
194
195 GNUNET_break (GNUNET_OK == success);
196 sm->value++;
197 stat_run (sm, sm->op, sm->stat, NULL);
198}
199
200
201/**
202 * Adapter function called to establish a connection to
203 * statistics service.
204 *
205 * @param cls closure
206 * @param cfg configuration of the peer to connect to; will be available until
207 * GNUNET_TESTBED_operation_done() is called on the operation returned
208 * from GNUNET_TESTBED_service_connect()
209 * @return service handle to return in 'op_result', NULL on error
210 */
211static void *
212statistics_connect_adapter (void *cls,
213 const struct GNUNET_CONFIGURATION_Handle *cfg)
214{
215 return GNUNET_STATISTICS_create ("<driver>",
216 cfg);
217}
218
219
220/**
221 * Adapter function called to destroy a connection to
222 * statistics service.
223 *
224 * @param cls closure
225 * @param op_result service handle returned from the connect adapter
226 */
227static void
228statistics_disconnect_adapter (void *cls,
229 void *op_result)
230{
231 GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
232}
233
234
235/**
236 * Function that gathers stats from all daemons.
237 */
238static void
239stat_run (void *cls,
240 struct GNUNET_TESTBED_Operation *op,
241 void *ca_result,
242 const char *emsg)
243{
244 struct StatMaster *sm = cls;
245
246 sm->stat = ca_result;
247 GNUNET_assert (NULL != sm->stat);
248 if (NULL != stats[sm->value].name)
249 {
250 GNUNET_STATISTICS_get (sm->stat,
251#if 0
252 NULL, NULL,
253#else
254 stats[sm->value].subsystem, stats[sm->value].name,
255#endif
256 &get_done, &print_stat,
257 sm);
258 return;
259 }
260 GNUNET_TESTBED_operation_done (sm->op);
261 sm->value = 0;
262 sm->daemon++;
263 if (NUM_DAEMONS == sm->daemon)
264 {
265 GNUNET_free (sm);
266 cleanup ();
267 return;
268 }
269 sm->op =
270 GNUNET_TESTBED_service_connect (NULL,
271 daemons[sm->daemon],
272 "statistics",
273 &stat_run, sm,
274 &statistics_connect_adapter,
275 &statistics_disconnect_adapter,
276 NULL);
277}
278
279
280static void
281do_report (void *cls)
282{
283 static int download_counter;
284 const char *type = cls;
285 struct GNUNET_TIME_Relative del;
286 char *fancy;
287 struct StatMaster *sm;
288
289 if (0 ==
290 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
291 TIMEOUT)).
292 rel_value_us)
293 {
294 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
295 "Timeout during download for type `%s', shutting down with error\n",
296 type);
297 ok = 1;
298 cleanup ();
299 return;
300 }
301 del = GNUNET_TIME_absolute_get_duration (start_time);
302 if (del.rel_value_us == 0)
303 del.rel_value_us = 1;
304 fancy =
305 GNUNET_STRINGS_byte_size_fancy (((unsigned long long) FILESIZE)
306 * 1000000LL / del.rel_value_us);
307 fprintf (stderr, "Download speed of type `%s' was %s/s\n", type, fancy);
308 GNUNET_free (fancy);
309 if (NUM_DAEMONS != ++download_counter)
310 return; /* more downloads to come */
311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
312 "Finished all downloads, getting statistics\n");
313 sm = GNUNET_new (struct StatMaster);
314 sm->op =
315 GNUNET_TESTBED_service_connect (NULL,
316 daemons[sm->daemon],
317 "statistics",
318 &stat_run, sm,
319 &statistics_connect_adapter,
320 &statistics_disconnect_adapter,
321 NULL);
322}
323
324
325static void
326do_downloads (void *cls, const struct GNUNET_FS_Uri *u2,
327 const char *fn)
328{
329 int anonymity;
330 unsigned int i;
331
332 if (NULL == u2)
333 {
334 cleanup ();
335 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
336 "Timeout during upload attempt, shutting down with error\n");
337 ok = 1;
338 return;
339 }
340 if (NULL != fn)
341 fn2 = GNUNET_strdup (fn);
342 uri2 = GNUNET_FS_uri_dup (u2);
343 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Downloading %llu bytes\n",
344 (unsigned long long) FILESIZE);
345 start_time = GNUNET_TIME_absolute_get ();
346 if (NULL != strstr (progname, "dht"))
347 anonymity = 0;
348 else
349 anonymity = 1;
350 /* (semi) leach-download(s); not true leaches since
351 * these peers do participate in sharing, they just
352 * don't have to offer anything *initially*. */
353 for (i = 0; i < NUM_DAEMONS - 2; i++)
354 GNUNET_FS_TEST_download (daemons[i], TIMEOUT, anonymity,
355 0 == (i % 2) ? SEED1 : SEED2,
356 0 == (i % 2) ? uri1 : uri2, VERBOSE, &do_report,
357 "leach");
358 /* mutual downloads of (primary) sharing peers */
359 GNUNET_FS_TEST_download (daemons[NUM_DAEMONS - 2], TIMEOUT, anonymity, SEED1,
360 uri1, VERBOSE, &do_report, "seeder 2");
361 GNUNET_FS_TEST_download (daemons[NUM_DAEMONS - 1], TIMEOUT, anonymity, SEED2,
362 uri2, VERBOSE, &do_report, "seeder 1");
363}
364
365
366static void
367do_publish2 (void *cls,
368 const struct GNUNET_FS_Uri *u1,
369 const char *fn)
370{
371 int do_index;
372 int anonymity;
373
374 if (NULL == u1)
375 {
376 cleanup ();
377 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
378 "Timeout during upload attempt, shutting down with error\n");
379 ok = 1;
380 return;
381 }
382 if (NULL != fn)
383 fn1 = GNUNET_strdup (fn);
384 uri1 = GNUNET_FS_uri_dup (u1);
385 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
386 (unsigned long long) FILESIZE);
387 if (NULL != strstr (progname, "index"))
388 do_index = GNUNET_YES;
389 else
390 do_index = GNUNET_NO;
391 if (NULL != strstr (progname, "dht"))
392 anonymity = 0;
393 else
394 anonymity = 1;
395
396 GNUNET_FS_TEST_publish (daemons[NUM_DAEMONS - 2], TIMEOUT, anonymity,
397 do_index, FILESIZE, SEED2, VERBOSE, &do_downloads,
398 NULL);
399}
400
401
402static void
403do_publish1 (void *cls,
404 struct GNUNET_TESTBED_Operation *op,
405 const char *emsg)
406{
407 unsigned int *coco = cls;
408 int do_index;
409 int anonymity;
410
411 GNUNET_TESTBED_operation_done (op);
412 if (NULL != emsg)
413 {
414 cleanup ();
415 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error trying to connect: %s\n", emsg);
416 ok = 1;
417 return;
418 }
419 if (0 != (--(*coco)))
420 return; /* more connections to be created */
421 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
422 (unsigned long long) FILESIZE);
423 if (NULL != strstr (progname, "index"))
424 do_index = GNUNET_YES;
425 else
426 do_index = GNUNET_NO;
427 if (NULL != strstr (progname, "dht"))
428 anonymity = 0;
429 else
430 anonymity = 1;
431 GNUNET_FS_TEST_publish (daemons[NUM_DAEMONS - 1], TIMEOUT, anonymity,
432 do_index, FILESIZE, SEED1, VERBOSE, &do_publish2,
433 NULL);
434}
435
436
437static void
438do_connect (void *cls,
439 struct GNUNET_TESTBED_RunHandle *h,
440 unsigned int num_peers,
441 struct GNUNET_TESTBED_Peer **peers,
442 unsigned int links_succeeded,
443 unsigned int links_failed)
444{
445 static unsigned int coco;
446 unsigned int i;
447 unsigned int j;
448
449 GNUNET_assert (NUM_DAEMONS == num_peers);
450 for (i = 0; i < num_peers; i++)
451 daemons[i] = peers[i];
452 for (i = 0; i < NUM_DAEMONS; i++)
453 for (j = i + 1; j < NUM_DAEMONS; j++)
454 {
455 coco++;
456 GNUNET_TESTBED_overlay_connect (NULL,
457 &do_publish1,
458 &coco,
459 peers[i],
460 peers[j]);
461 }
462}
463
464
465int
466main (int argc, char *argv[])
467{
468 progname = argv[0];
469 (void) GNUNET_TESTBED_test_run ("perf-gnunet-service-fs-p2p-respect",
470 "perf_gnunet_service_fs_p2p.conf",
471 NUM_DAEMONS,
472 0, NULL, NULL,
473 &do_connect, NULL);
474 GNUNET_DISK_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 */
53static struct GNUNET_BLOCK_Group *
54block_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 */
120static enum GNUNET_GenericReturnValue
121block_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 */
168static enum GNUNET_GenericReturnValue
169block_plugin_fs_check_query (void *cls,
170 enum GNUNET_BLOCK_Type type,
171 const struct GNUNET_HashCode *query,
172 const void *xquery,
173 size_t xquery_size)
174{
175 switch (type)
176 {
177 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
178 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
179 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
180 if (0 != xquery_size)
181 {
182 GNUNET_break_op (0);
183 return GNUNET_NO;
184 }
185 return GNUNET_OK;
186 default:
187 GNUNET_break (0);
188 return GNUNET_SYSERR;
189 }
190}
191
192
193/**
194 * Function called to validate a block for storage.
195 *
196 * @param cls closure
197 * @param type block type
198 * @param block block data to validate
199 * @param block_size number of bytes in @a block
200 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
201 */
202static enum GNUNET_GenericReturnValue
203block_plugin_fs_check_block (void *cls,
204 enum GNUNET_BLOCK_Type type,
205 const void *block,
206 size_t block_size)
207{
208 switch (type)
209 {
210 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
211 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
212 return GNUNET_OK;
213 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
214 {
215 const struct UBlock *ub;
216
217 if (block_size < sizeof(struct UBlock))
218 {
219 GNUNET_break_op (0);
220 return GNUNET_NO;
221 }
222 ub = block;
223 if (block_size !=
224 ntohl (ub->purpose.size)
225 + sizeof (struct GNUNET_CRYPTO_EcdsaSignature))
226 {
227 GNUNET_break_op (0);
228 return GNUNET_NO;
229 }
230 if (GNUNET_OK !=
231 GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_FS_UBLOCK,
232 &ub->purpose,
233 &ub->signature,
234 &ub->verification_key))
235 {
236 GNUNET_break_op (0);
237 return GNUNET_NO;
238 }
239 return GNUNET_OK;
240 }
241 default:
242 GNUNET_break (0);
243 return GNUNET_SYSERR;
244 }
245}
246
247
248/**
249 * Function called to validate a reply to a request. Note that it is assumed
250 * that the reply has already been matched to the key (and signatures checked)
251 * as it would be done with the GetKeyFunction and the
252 * BlockEvaluationFunction.
253 *
254 * @param cls closure
255 * @param type block type
256 * @param group which block group to use for evaluation
257 * @param query original query (hash)
258 * @param xquery extrended query data (can be NULL, depending on type)
259 * @param xquery_size number of bytes in @a xquery
260 * @param reply_block response to validate
261 * @param reply_block_size number of bytes in @a reply_block
262 * @return characterization of result
263 */
264static enum GNUNET_BLOCK_ReplyEvaluationResult
265block_plugin_fs_check_reply (void *cls,
266 enum GNUNET_BLOCK_Type type,
267 struct GNUNET_BLOCK_Group *group,
268 const struct GNUNET_HashCode *query,
269 const void *xquery,
270 size_t xquery_size,
271 const void *reply_block,
272 size_t reply_block_size)
273{
274 switch (type)
275 {
276 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
277 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
278 return GNUNET_BLOCK_REPLY_OK_LAST;
279 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
280 {
281 struct GNUNET_HashCode chash;
282
283 GNUNET_CRYPTO_hash (reply_block,
284 reply_block_size,
285 &chash);
286 if (GNUNET_YES ==
287 GNUNET_BLOCK_GROUP_bf_test_and_set (group,
288 &chash))
289 return GNUNET_BLOCK_REPLY_OK_DUPLICATE;
290 return GNUNET_BLOCK_REPLY_OK_MORE;
291 }
292 default:
293 GNUNET_break (0);
294 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
295 }
296}
297
298
299/**
300 * Entry point for the plugin.
301 */
302void *
303libgnunet_plugin_block_fs_init (void *cls)
304{
305 static const enum GNUNET_BLOCK_Type types[] = {
306 GNUNET_BLOCK_TYPE_FS_DBLOCK,
307 GNUNET_BLOCK_TYPE_FS_IBLOCK,
308 GNUNET_BLOCK_TYPE_FS_UBLOCK,
309 GNUNET_BLOCK_TYPE_ANY /* end of list */
310 };
311 struct GNUNET_BLOCK_PluginFunctions *api;
312
313 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
314 api->get_key = &block_plugin_fs_get_key;
315 api->create_group = &block_plugin_fs_create_group;
316 api->check_query = &block_plugin_fs_check_query;
317 api->check_block = &block_plugin_fs_check_block;
318 api->check_reply = &block_plugin_fs_check_reply;
319 api->types = types;
320 return api;
321}
322
323
324/**
325 * Exit point from the plugin.
326 */
327void *
328libgnunet_plugin_block_fs_done (void *cls)
329{
330 struct GNUNET_BLOCK_PluginFunctions *api = cls;
331
332 GNUNET_free (api);
333 return NULL;
334}
335
336
337/* end of plugin_block_fs.c */
diff --git a/src/fs/test_fs.c b/src/fs/test_fs.c
deleted file mode 100644
index 7a57e98b0..000000000
--- a/src/fs/test_fs.c
+++ /dev/null
@@ -1,262 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs.c
23 * @brief testcase for FS (upload-search-download-unindex)
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util.h"
29#include "gnunet_fsui_lib.h"
30
31#define DEBUG_VERBOSE GNUNET_NO
32
33#define CHECK(a) if (! (a)) { ok = GNUNET_NO; GNUNET_GE_BREAK (NULL, 0); \
34 goto FAILURE; }
35
36static char *
37makeName (unsigned int i)
38{
39 char *fn;
40
41 fn = GNUNET_malloc (strlen ("/tmp/gnunet-basic_fsui_test/BASIC_FSUI_TEST")
42 + 14);
43 GNUNET_snprintf (fn,
44 strlen ("/tmp/gnunet-basic_fsui_test/BASIC_FSUI_TEST") + 14,
45 "/tmp/gnunet-basic_fsui_test/BASIC_FSUI_TEST%u", i);
46 GNUNET_disk_directory_create_for_file (NULL, fn);
47 return fn;
48}
49
50
51static enum GNUNET_FSUI_EventType lastEvent;
52
53static struct GNUNET_MetaData *search_meta;
54
55static struct GNUNET_ECRS_URI *search_uri;
56
57static struct GNUNET_FSUI_Context *ctx;
58
59static void *
60eventCallback (void *cls, const GNUNET_FSUI_Event *event)
61{
62 static char unused;
63
64 switch (event->type)
65 {
66 case GNUNET_FSUI_search_resumed:
67 case GNUNET_FSUI_download_resumed:
68 case GNUNET_FSUI_upload_resumed:
69 case GNUNET_FSUI_unindex_resumed:
70 return &unused;
71
72 case GNUNET_FSUI_search_result:
73#if DEBUG_VERBOSE
74 printf ("Received search result\n");
75#endif
76 search_uri = GNUNET_ECRS_uri_duplicate (event->data.SearchResult.fi.uri);
77 search_meta = GNUNET_meta_data_duplicate (event->data.SearchResult.fi.meta);
78 break;
79
80 case GNUNET_FSUI_upload_completed:
81#if DEBUG_VERBOSE
82 printf ("Upload complete.\n");
83#endif
84 break;
85
86 case GNUNET_FSUI_download_completed:
87#if DEBUG_VERBOSE
88 printf ("Download complete.\n");
89#endif
90 break;
91
92 case GNUNET_FSUI_unindex_completed:
93#if DEBUG_VERBOSE
94 printf ("Unindex complete.\n");
95#endif
96 break;
97
98 default:
99 break;
100 }
101 lastEvent = event->type;
102 return NULL;
103}
104
105
106#define START_DAEMON 1
107
108int
109main (int argc, char *argv[])
110{
111#if START_DAEMON
112 struct GNUNET_OS_Process *daemon;
113#endif
114 int ok;
115 struct GNUNET_ECRS_URI *uri;
116 char *filename = NULL;
117
118 char *keywords[] = {
119 "fsui_foo",
120 "fsui_bar",
121 };
122 char keyword[40];
123 char *fn;
124 int prog;
125 struct GNUNET_MetaData *meta;
126 struct GNUNET_ECRS_URI *kuri;
127 struct GNUNET_GC_Configuration *cfg;
128 struct GNUNET_FSUI_UploadList *upload = NULL;
129 struct GNUNET_FSUI_SearchList *search = NULL;
130 struct GNUNET_FSUI_UnindexList *unindex = NULL;
131 struct GNUNET_FSUI_DownloadList *download = NULL;
132
133 cfg = GNUNET_GC_create ();
134 if (-1 == GNUNET_GC_parse_configuration (cfg, "check.conf"))
135 {
136 GNUNET_GC_free (cfg);
137 return -1;
138 }
139#if START_DAEMON
140 daemon = GNUNET_daemon_start (NULL, cfg, "peer.conf", GNUNET_NO);
141 GNUNET_GE_ASSERT (NULL, daemon != NULL);
142 CHECK (GNUNET_OK ==
143 GNUNET_wait_for_daemon_running (NULL, cfg, 60 * GNUNET_CRON_SECONDS));
144#endif
145 GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS); /* give apps time to start */
146 ok = GNUNET_YES;
147
148 /* ACTUAL TEST CODE */
149 ctx = GNUNET_FSUI_start (NULL, cfg, "basic_fsui_test", 32, /* thread pool size */
150 GNUNET_NO, /* no resume */
151 &eventCallback, NULL);
152 CHECK (ctx != NULL);
153 filename = makeName (42);
154 GNUNET_disk_file_write (NULL, filename, "foo bar test!",
155 strlen ("foo bar test!"), "600");
156 meta = GNUNET_meta_data_create ();
157 kuri =
158 GNUNET_ECRS_keyword_command_line_to_uri (NULL, 2,
159 (const char **) keywords);
160 /* upload */
161 upload = GNUNET_FSUI_upload_start (ctx, filename,
162 (GNUNET_FSUI_DirectoryScanCallback)
163 & GNUNET_disk_directory_scan, NULL, 0, /* anonymity */
164 0, /* priority */
165 GNUNET_YES, GNUNET_NO, GNUNET_NO,
166 GNUNET_get_time () + 5 * GNUNET_CRON_HOURS,
167 meta, kuri, kuri);
168 CHECK (upload != NULL);
169 GNUNET_ECRS_uri_destroy (kuri);
170 GNUNET_meta_data_destroy (meta);
171 prog = 0;
172 while (lastEvent != GNUNET_FSUI_upload_completed)
173 {
174 prog++;
175 CHECK (prog < 10000) GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS);
176 if (GNUNET_shutdown_test () == GNUNET_YES)
177 break;
178 }
179
180 /* search */
181 GNUNET_snprintf (keyword, 40, "+%s +%s", keywords[0], keywords[1]);
182 uri = GNUNET_ECRS_keyword_string_to_uri (NULL, keyword);
183 search = GNUNET_FSUI_search_start (ctx, 0, uri);
184 GNUNET_ECRS_uri_destroy (uri);
185 CHECK (search != NULL);
186 prog = 0;
187 while (lastEvent != GNUNET_FSUI_search_result)
188 {
189 prog++;
190 CHECK (prog < 10000);
191 GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS);
192 if (GNUNET_shutdown_test () == GNUNET_YES)
193 break;
194 }
195 GNUNET_FSUI_search_abort (search);
196 GNUNET_FSUI_search_stop (search);
197
198 /* download */
199 fn = makeName (43);
200 download =
201 GNUNET_FSUI_download_start (ctx, 0, GNUNET_NO, search_uri, search_meta,
202 fn, NULL, NULL);
203 GNUNET_free (fn);
204 prog = 0;
205 while (lastEvent != GNUNET_FSUI_download_completed)
206 {
207 prog++;
208 CHECK (prog < 10000);
209 GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS);
210 if (GNUNET_shutdown_test () == GNUNET_YES)
211 break;
212 }
213 GNUNET_FSUI_download_stop (download);
214 download = NULL;
215 GNUNET_ECRS_uri_destroy (search_uri);
216 GNUNET_meta_data_destroy (search_meta);
217 /* unindex */
218 unindex = GNUNET_FSUI_unindex_start (ctx, filename);
219 prog = 0;
220 while (lastEvent != GNUNET_FSUI_unindex_completed)
221 {
222 prog++;
223 CHECK (prog < 10000);
224 GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS);
225 if (GNUNET_shutdown_test () == GNUNET_YES)
226 break;
227 }
228 if (lastEvent != GNUNET_FSUI_unindex_completed)
229 GNUNET_FSUI_unindex_abort (unindex);
230 GNUNET_FSUI_unindex_stop (unindex);
231
232
233 /* END OF TEST CODE */
234FAILURE:
235 if (ctx != NULL)
236 GNUNET_FSUI_stop (ctx);
237 if (filename != NULL)
238 {
239 unlink (filename);
240 GNUNET_free (filename);
241 }
242 if (download != NULL)
243 {
244 GNUNET_FSUI_download_abort (download);
245 GNUNET_FSUI_download_stop (download);
246 }
247 filename = makeName (43);
248 /* TODO: verify file 'filename(42)' == file 'filename(43)' */
249 unlink (filename);
250 GNUNET_free (filename);
251
252#if START_DAEMON
253 GNUNET_GE_ASSERT (NULL, GNUNET_OK == GNUNET_daemon_stop (NULL, daemon));
254 GNUNET_OS_process_destroy (daemon);
255#endif
256 GNUNET_GC_free (cfg);
257
258 return (ok == GNUNET_YES) ? 0 : 1;
259}
260
261
262/* end of test_fs.c */
diff --git a/src/fs/test_fs_data.conf b/src/fs/test_fs_data.conf
deleted file mode 100644
index 993141064..000000000
--- a/src/fs/test_fs_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-data/
4
5[fs]
6ACTIVEMIGRATION = NO
7
diff --git a/src/fs/test_fs_defaults.conf b/src/fs/test_fs_defaults.conf
deleted file mode 100644
index 6ead78257..000000000
--- a/src/fs/test_fs_defaults.conf
+++ /dev/null
@@ -1,47 +0,0 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-lib/
5
6[transport]
7PLUGINS = tcp
8
9[nat]
10DISABLEV6 = YES
11ENABLE_UPNP = NO
12BEHIND_NAT = NO
13ALLOW_NAT = NO
14INTERNAL_ADDRESS = 127.0.0.1
15EXTERNAL_ADDRESS = 127.0.0.1
16USE_LOCALADDR = YES
17RETURN_LOCAL_ADDRESSES = YES
18
19[datastore]
20QUOTA = 100 MB
21
22[transport-tcp]
23BINDTO = 127.0.0.1
24PORT = 54368
25
26[peerinfo]
27NO_IO = YES
28
29[ats]
30WAN_QUOTA_IN = 65536
31WAN_QUOTA_OUT = 65536
32
33[fs]
34CONTENT_CACHING = YES
35CONTENT_PUSHING = YES
36DELAY = YES
37# PREFIX = valgrind --leak-check=full
38
39[dhtcache]
40QUOTA=65536
41DATABASE=heap
42
43[cadet]
44REFRESH_PATH_TIME = 30 min
45
46[nse]
47WORKBITS = 0
diff --git a/src/fs/test_fs_directory.c b/src/fs/test_fs_directory.c
deleted file mode 100644
index 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
36struct PCLS
37{
38 struct GNUNET_FS_Uri **uri;
39 struct GNUNET_FS_MetaData **md;
40 unsigned int pos;
41 unsigned int max;
42};
43
44static void
45processor (void *cls, const char *filename, const struct GNUNET_FS_Uri *uri,
46 const struct GNUNET_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
67static int
68testDirectory (unsigned int i)
69{
70 struct GNUNET_FS_DirectoryBuilder *db;
71 char *data;
72 size_t dlen;
73 struct GNUNET_FS_Uri **uris;
74 struct GNUNET_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
165int
166main (int argc, char *argv[])
167{
168 int failureCount = 0;
169 int i;
170
171 GNUNET_log_setup ("test_fs_directory",
172#if VERBOSE
173 "DEBUG",
174#else
175 "WARNING",
176#endif
177 NULL);
178 for (i = 17; i < 1000; i *= 2)
179 failureCount += testDirectory (i);
180 if (failureCount != 0)
181 return 1;
182 return 0;
183}
184
185
186/* end of test_fs_directory.c */
diff --git a/src/fs/test_fs_download.c b/src/fs/test_fs_download.c
deleted file mode 100644
index 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
48static unsigned int anonymity_level;
49
50static int indexed;
51
52static struct GNUNET_TIME_Absolute start;
53
54static struct GNUNET_FS_Handle *fs;
55
56static struct GNUNET_FS_DownloadContext *download;
57
58static struct GNUNET_FS_PublishContext *publish;
59
60static struct GNUNET_SCHEDULER_Task *timeout_kill;
61
62static char *fn;
63
64static char *fn1;
65
66static int err;
67
68
69static void
70timeout_kill_task (void *cls)
71{
72 if (NULL != download)
73 {
74 GNUNET_FS_download_stop (download, GNUNET_YES);
75 download = NULL;
76 }
77 else if (NULL != publish)
78 {
79 GNUNET_FS_publish_stop (publish);
80 publish = NULL;
81 }
82 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Timeout downloading file\n");
83 timeout_kill = NULL;
84 err = 1;
85}
86
87
88static void
89abort_publish_task (void *cls)
90{
91 if (NULL != publish)
92 {
93 GNUNET_FS_publish_stop (publish);
94 publish = NULL;
95 }
96}
97
98
99static void
100stop_fs_task (void *cls)
101{
102 GNUNET_FS_stop (fs);
103 fs = NULL;
104}
105
106
107static void
108abort_download_task (void *cls)
109{
110 uint64_t size;
111
112 if (NULL != download)
113 {
114 GNUNET_FS_download_stop (download, GNUNET_YES);
115 download = NULL;
116 }
117 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_size (fn, &size, GNUNET_YES,
118 GNUNET_NO));
119 GNUNET_assert (size == FILESIZE);
120 GNUNET_DISK_directory_remove (fn);
121 GNUNET_free (fn);
122 fn = NULL;
123 GNUNET_SCHEDULER_cancel (timeout_kill);
124 timeout_kill = NULL;
125}
126
127
128static void *
129progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
130{
131 switch (event->status)
132 {
133 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
134 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
135 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
136 (unsigned long long) event->value.publish.completed,
137 (unsigned long long) event->value.publish.size,
138 event->value.publish.specifics.progress.depth,
139 (unsigned long long) event->value.publish.specifics.
140 progress.offset);
141 break;
142
143 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
144 break;
145
146 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
147 fprintf (stdout,
148 "Publishing complete, %llu kb/s.\n",
149 (unsigned long long) (FILESIZE * 1000000LL
150 / (1
151 + GNUNET_TIME_absolute_get_duration
152 (start).rel_value_us) / 1024LL));
153 GAUGER ("FS",
154 (GNUNET_YES == indexed)
155 ? "Publishing speed (indexing)"
156 : "Publishing speed (insertion)",
157 (unsigned long long) (FILESIZE * 1000000LL
158 / (1
159 + GNUNET_TIME_absolute_get_duration
160 (start).rel_value_us) / 1024LL), "kb/s");
161 fn = GNUNET_DISK_mktemp ("gnunet-download-test-dst");
162 start = GNUNET_TIME_absolute_get ();
163 download =
164 GNUNET_FS_download_start (fs,
165 event->value.publish.specifics.
166 completed.chk_uri, NULL, fn, NULL, 0,
167 FILESIZE, anonymity_level,
168 GNUNET_FS_DOWNLOAD_OPTION_NONE,
169 "download", NULL);
170 GNUNET_assert (download != NULL);
171 break;
172
173 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
174 fprintf (stdout,
175 "Download complete, %llu kb/s.\n",
176 (unsigned long long) (FILESIZE * 1000000LL
177 / (1
178 + GNUNET_TIME_absolute_get_duration
179 (start).rel_value_us) / 1024LL));
180 GAUGER ("FS",
181 (GNUNET_YES == indexed)
182 ? "Local download speed (indexed)"
183 : "Local download speed (inserted)",
184 (unsigned long long) (FILESIZE * 1000000LL
185 / (1
186 + GNUNET_TIME_absolute_get_duration
187 (start).rel_value_us) / 1024LL), "kb/s");
188 GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
189 break;
190
191 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
192 GNUNET_assert (download == event->value.download.dc);
193 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
194 "Download is progressing (%llu/%llu at level %u off %llu)...\n",
195 (unsigned long long) event->value.download.completed,
196 (unsigned long long) event->value.download.size,
197 event->value.download.specifics.progress.depth,
198 (unsigned long long) event->value.download.specifics.
199 progress.offset);
200 break;
201
202 case GNUNET_FS_STATUS_PUBLISH_ERROR:
203 fprintf (stderr, "Error publishing file: %s\n",
204 event->value.publish.specifics.error.message);
205 GNUNET_break (0);
206 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
207 GNUNET_SCHEDULER_shutdown ();
208 break;
209
210 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
211 fprintf (stderr, "Error downloading file: %s\n",
212 event->value.download.specifics.error.message);
213 GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
214 GNUNET_SCHEDULER_shutdown ();
215 break;
216
217 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
218 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
219 break;
220
221 case GNUNET_FS_STATUS_PUBLISH_START:
222 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
223 GNUNET_assert (NULL == event->value.publish.pctx);
224 GNUNET_assert (FILESIZE == event->value.publish.size);
225 GNUNET_assert (0 == event->value.publish.completed);
226 GNUNET_assert (1 == event->value.publish.anonymity);
227 break;
228
229 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
230 GNUNET_assert (publish == event->value.publish.pc);
231 GNUNET_assert (FILESIZE == event->value.publish.size);
232 GNUNET_assert (1 == event->value.publish.anonymity);
233 GNUNET_SCHEDULER_add_now (&stop_fs_task, NULL);
234 break;
235
236 case GNUNET_FS_STATUS_DOWNLOAD_START:
237 GNUNET_assert (0 == strcmp ("download", event->value.download.cctx));
238 GNUNET_assert (NULL == event->value.download.pctx);
239 GNUNET_assert (NULL != event->value.download.uri);
240 GNUNET_assert (0 == strcmp (fn, event->value.download.filename));
241 GNUNET_assert (FILESIZE == event->value.download.size);
242 GNUNET_assert (0 == event->value.download.completed);
243 GNUNET_assert (1 == event->value.download.anonymity);
244 break;
245
246 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
247 GNUNET_assert (download == event->value.download.dc);
248 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
249 break;
250
251 default:
252 printf ("Unexpected event: %d\n", event->status);
253 break;
254 }
255 return NULL;
256}
257
258
259static void
260run (void *cls,
261 const struct GNUNET_CONFIGURATION_Handle *cfg,
262 struct GNUNET_TESTING_Peer *peer)
263{
264 const char *binary_name = cls;
265 const char *keywords[] = {
266 "down_foo",
267 "down_bar",
268 };
269 char *buf;
270 struct GNUNET_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
337int
338main (int argc, char *argv[])
339{
340 const char *binary_name;
341 const char *config_name;
342
343 binary_name = "test-fs-download";
344 config_name = "test_fs_download_data.conf";
345 if (NULL != strstr (argv[0], "indexed"))
346 {
347 binary_name = "test-fs-download-indexed";
348 config_name = "test_fs_download_indexed.conf";
349 }
350 if (NULL != strstr (argv[0], "cadet"))
351 {
352 binary_name = "test-fs-download-cadet";
353 config_name = "test_fs_download_cadet.conf";
354 }
355 if (0 != GNUNET_TESTING_peer_run (binary_name,
356 config_name,
357 &run, (void *) binary_name))
358 return 1;
359 if (NULL != fn1)
360 {
361 unlink (fn1);
362 GNUNET_free (fn1);
363 }
364 return err;
365}
366
367
368/* end of test_fs_download.c */
diff --git a/src/fs/test_fs_download_data.conf b/src/fs/test_fs_download_data.conf
deleted file mode 100644
index 160dec3ae..000000000
--- a/src/fs/test_fs_download_data.conf
+++ /dev/null
@@ -1,10 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-download/
4
5[download-test]
6# set to 'YES' to test non-anonymous download
7USE_STREAM = NO
8
9# set to 'YES' to use indexing
10USE_INDEX = NO \ No newline at end of file
diff --git a/src/fs/test_fs_download_indexed.conf b/src/fs/test_fs_download_indexed.conf
deleted file mode 100644
index 7f1e36935..000000000
--- a/src/fs/test_fs_download_indexed.conf
+++ /dev/null
@@ -1,10 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-download/
4
5[download-test]
6# set to 'YES' to test non-anonymous download
7USE_STREAM = NO
8
9# set to 'YES' to use indexing
10USE_INDEX = YES
diff --git a/src/fs/test_fs_download_persistence.c b/src/fs/test_fs_download_persistence.c
deleted file mode 100644
index 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
47static struct GNUNET_TIME_Absolute start;
48
49static const struct GNUNET_CONFIGURATION_Handle *cfg;
50
51static struct GNUNET_FS_Handle *fs;
52
53static struct GNUNET_FS_DownloadContext *download;
54
55static struct GNUNET_FS_PublishContext *publish;
56
57static struct GNUNET_SCHEDULER_Task *timeout_kill;
58
59static char *fn;
60
61static int err;
62
63
64static void
65timeout_kill_task (void *cls)
66{
67 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Timeout downloading file\n");
68 if (download != NULL)
69 {
70 GNUNET_FS_download_stop (download, GNUNET_YES);
71 download = NULL;
72 }
73 else if (publish != NULL)
74 {
75 GNUNET_FS_publish_stop (publish);
76 publish = NULL;
77 }
78 timeout_kill = NULL;
79 err = 1;
80}
81
82
83static void
84abort_publish_task (void *cls)
85{
86 if (publish != NULL)
87 {
88 GNUNET_FS_publish_stop (publish);
89 publish = NULL;
90 }
91}
92
93
94static void
95abort_download_task (void *cls)
96{
97 uint64_t size;
98
99 if (download != NULL)
100 {
101 GNUNET_FS_download_stop (download, GNUNET_YES);
102 download = NULL;
103 }
104 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_size (fn, &size, GNUNET_YES,
105 GNUNET_NO));
106 GNUNET_assert (size == FILESIZE);
107 GNUNET_DISK_directory_remove (fn);
108 GNUNET_free (fn);
109 fn = NULL;
110 GNUNET_SCHEDULER_cancel (timeout_kill);
111 timeout_kill = NULL;
112}
113
114
115static void *
116progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event);
117
118
119static void
120restart_fs_task (void *cls)
121{
122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Restarting FS.\n");
123 GNUNET_FS_stop (fs);
124 fs = GNUNET_FS_start (cfg, "test-fs-download-persistence", &progress_cb, NULL,
125 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
126}
127
128
129/**
130 * Consider scheduling the restart-task.
131 * Only runs the restart task once per event
132 * category.
133 *
134 * @param ev type of the event to consider
135 */
136static void
137consider_restart (int ev)
138{
139 static int prev[32];
140 static int off;
141 int i;
142
143 for (i = 0; i < off; i++)
144 if (prev[i] == ev)
145 return;
146 prev[off++] = ev;
147 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_URGENT,
148 &restart_fs_task, NULL);
149}
150
151
152static void *
153progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
154{
155 switch (event->status)
156 {
157 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
159 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
160 (unsigned long long) event->value.publish.completed,
161 (unsigned long long) event->value.publish.size,
162 event->value.publish.specifics.progress.depth,
163 (unsigned long long) event->value.publish.specifics.
164 progress.offset);
165 break;
166
167 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
168 break;
169
170 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
171 printf ("Publishing complete, %llu kbps.\n",
172 (unsigned long long) (FILESIZE * 1000000LL
173 / (1
174 + GNUNET_TIME_absolute_get_duration
175 (start).rel_value_us) / 1024LL));
176 fn = GNUNET_DISK_mktemp ("gnunet-download-test-dst");
177 start = GNUNET_TIME_absolute_get ();
178 GNUNET_assert (download == NULL);
179 GNUNET_FS_download_start (fs,
180 event->value.publish.specifics.completed.chk_uri,
181 NULL, fn, NULL, 0, FILESIZE, 1,
182 GNUNET_FS_DOWNLOAD_OPTION_NONE, "download", NULL);
183 break;
184
185 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
186 printf ("Download complete, %llu kbps.\n",
187 (unsigned long long) (FILESIZE * 1000000LL
188 / (1
189 + GNUNET_TIME_absolute_get_duration
190 (start).rel_value_us) / 1024LL));
191 GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
192 break;
193
194 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
195 consider_restart (event->status);
196 GNUNET_assert (download == event->value.download.dc);
197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
198 "Download is progressing (%llu/%llu at level %u off %llu)...\n",
199 (unsigned long long) event->value.download.completed,
200 (unsigned long long) event->value.download.size,
201 event->value.download.specifics.progress.depth,
202 (unsigned long long) event->value.download.specifics.
203 progress.offset);
204 break;
205
206 case GNUNET_FS_STATUS_PUBLISH_ERROR:
207 fprintf (stderr, "Error publishing file: %s\n",
208 event->value.publish.specifics.error.message);
209 GNUNET_break (0);
210 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
211 break;
212
213 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
214 fprintf (stderr, "Error downloading file: %s\n",
215 event->value.download.specifics.error.message);
216 GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
217 break;
218
219 case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
220 GNUNET_assert (event->value.publish.pc == publish);
221 publish = NULL;
222 break;
223
224 case GNUNET_FS_STATUS_PUBLISH_RESUME:
225 GNUNET_assert (NULL == publish);
226 publish = event->value.publish.pc;
227 break;
228
229 case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download suspended.\n");
231 GNUNET_assert (event->value.download.dc == download);
232 download = NULL;
233 break;
234
235 case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
236 GNUNET_assert (NULL == download);
237 download = event->value.download.dc;
238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download resumed.\n");
239 break;
240
241 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
242 consider_restart (event->status);
243 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download active.\n");
244 break;
245
246 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
247 consider_restart (event->status);
248 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download inactive.\n");
249 break;
250
251 case GNUNET_FS_STATUS_PUBLISH_START:
252 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
253 GNUNET_assert (NULL == event->value.publish.pctx);
254 GNUNET_assert (FILESIZE == event->value.publish.size);
255 GNUNET_assert (0 == event->value.publish.completed);
256 GNUNET_assert (1 == event->value.publish.anonymity);
257 break;
258
259 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
260 GNUNET_assert (publish == event->value.publish.pc);
261 GNUNET_assert (FILESIZE == event->value.publish.size);
262 GNUNET_assert (1 == event->value.publish.anonymity);
263 GNUNET_FS_stop (fs);
264 fs = NULL;
265 break;
266
267 case GNUNET_FS_STATUS_DOWNLOAD_START:
268 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download started.\n");
269 consider_restart (event->status);
270 GNUNET_assert (download == NULL);
271 download = event->value.download.dc;
272 GNUNET_assert (0 == strcmp ("download", event->value.download.cctx));
273 GNUNET_assert (NULL == event->value.download.pctx);
274 GNUNET_assert (NULL != event->value.download.uri);
275 GNUNET_assert (0 == strcmp (fn, event->value.download.filename));
276 GNUNET_assert (FILESIZE == event->value.download.size);
277 GNUNET_assert (0 == event->value.download.completed);
278 GNUNET_assert (1 == event->value.download.anonymity);
279 break;
280
281 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
282 GNUNET_assert (download == event->value.download.dc);
283 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
284 download = NULL;
285 break;
286
287 default:
288 printf ("Unexpected event: %d\n", event->status);
289 break;
290 }
291 return NULL;
292}
293
294
295static void
296run (void *cls,
297 const struct GNUNET_CONFIGURATION_Handle *c,
298 struct GNUNET_TESTING_Peer *peer)
299{
300 const char *keywords[] = {
301 "down_foo",
302 "down_bar",
303 };
304 char *buf;
305 struct GNUNET_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
340int
341main (int argc, char *argv[])
342{
343 if (0 != GNUNET_TESTING_peer_run ("test-fs-download-persistence",
344 "test_fs_download_data.conf",
345 &run, NULL))
346 return 1;
347 return err;
348}
349
350
351/* end of test_fs_download_persistence.c */
diff --git a/src/fs/test_fs_file_information.c b/src/fs/test_fs_file_information.c
deleted file mode 100644
index 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
50static int
51mycleaner (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
59static void
60run (void *cls, char *const *args, const char *cfgfile,
61 const struct GNUNET_CONFIGURATION_Handle *cfg)
62{
63 const char *keywords[] = {
64 "down_foo",
65 "down_bar",
66 };
67 char *fn1;
68 char *fn2;
69 char *buf;
70 struct GNUNET_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
140int
141main (int argc, char *argv[])
142{
143 char *const argvx[] = {
144 "test-fs-file_information",
145 "-c",
146 "test_fs_file_information_data.conf",
147 NULL
148 };
149 struct GNUNET_GETOPT_CommandLineOption options[] = {
150 GNUNET_GETOPT_OPTION_END
151 };
152
153 GNUNET_log_setup ("test_fs_file_information",
154 "WARNING",
155 NULL);
156 GNUNET_PROGRAM_run ((sizeof(argvx) / sizeof(char *)) - 1, argvx,
157 "test-fs-file_information", "nohelp", options, &run,
158 NULL);
159 return 0;
160}
161
162
163/* end of test_fs_file_information.c */
diff --git a/src/fs/test_fs_file_information_data.conf b/src/fs/test_fs_file_information_data.conf
deleted file mode 100644
index c8fc0938c..000000000
--- a/src/fs/test_fs_file_information_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-file-information/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_fs_getopt.c b/src/fs/test_fs_getopt.c
deleted file mode 100644
index 3d0da752b..000000000
--- a/src/fs/test_fs_getopt.c
+++ /dev/null
@@ -1,37 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_getopt.c
22 * @brief test for fs_getopt.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_fs_service.h"
27
28
29int
30main (int argc, char *argv[])
31{
32 GNUNET_log_setup ("test_fs_getopt",
33 "WARNING",
34 NULL);
35 fprintf (stderr, "%s", "WARNING: testcase not yet written.\n");
36 return 0; /* testcase passed */
37}
diff --git a/src/fs/test_fs_list_indexed.c b/src/fs/test_fs_list_indexed.c
deleted file mode 100644
index 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
51static struct GNUNET_TIME_Absolute start;
52
53static struct GNUNET_FS_Handle *fs;
54
55static struct GNUNET_FS_PublishContext *publish;
56
57static char *fn1;
58
59static char *fn2;
60
61static int err;
62
63
64static void
65abort_publish_task (void *cls)
66{
67 GNUNET_FS_publish_stop (publish);
68 publish = NULL;
69 GNUNET_DISK_directory_remove (fn1);
70 GNUNET_free (fn1);
71 fn1 = NULL;
72 GNUNET_DISK_directory_remove (fn2);
73 GNUNET_free (fn2);
74 fn2 = NULL;
75}
76
77
78static void
79list_indexed_task (void *cls)
80{
81 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
82}
83
84
85static void *
86progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
87{
88 void *ret;
89
90 ret = NULL;
91 switch (event->status)
92 {
93 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
94 ret = event->value.publish.cctx;
95 printf ("Publish complete, %llu kbps.\n",
96 (unsigned long long) (FILESIZE * 1000000LL
97 / (1
98 + GNUNET_TIME_absolute_get_duration
99 (start).rel_value_us) / 1024));
100 if (0 == strcmp ("list_indexed-context-dir", event->value.publish.cctx))
101 GNUNET_SCHEDULER_add_now (&list_indexed_task, NULL);
102
103 break;
104
105 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
106 ret = event->value.publish.cctx;
107 GNUNET_assert (publish == event->value.publish.pc);
108 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
109 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
110 (unsigned long long) event->value.publish.completed,
111 (unsigned long long) event->value.publish.size,
112 event->value.publish.specifics.progress.depth,
113 (unsigned long long) event->value.publish.specifics.
114 progress.offset);
115 break;
116
117 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
118 ret = event->value.publish.cctx;
119 break;
120
121 case GNUNET_FS_STATUS_PUBLISH_ERROR:
122 ret = event->value.publish.cctx;
123 fprintf (stderr, "Error publishing file: %s\n",
124 event->value.publish.specifics.error.message);
125 err = 1;
126 if (0 == strcmp ("list_indexed-context-dir", event->value.publish.cctx))
127 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
128 break;
129
130 case GNUNET_FS_STATUS_PUBLISH_START:
131 ret = event->value.publish.cctx;
132 if (0 == strcmp ("list_indexed-context1", event->value.publish.cctx))
133 {
134 GNUNET_assert (0 ==
135 strcmp ("list_indexed-context-dir",
136 event->value.publish.pctx));
137 GNUNET_assert (FILESIZE == event->value.publish.size);
138 GNUNET_assert (0 == event->value.publish.completed);
139 GNUNET_assert (1 == event->value.publish.anonymity);
140 }
141 else if (0 == strcmp ("list_indexed-context2", event->value.publish.cctx))
142 {
143 GNUNET_assert (0 ==
144 strcmp ("list_indexed-context-dir",
145 event->value.publish.pctx));
146 GNUNET_assert (FILESIZE == event->value.publish.size);
147 GNUNET_assert (0 == event->value.publish.completed);
148 GNUNET_assert (2 == event->value.publish.anonymity);
149 }
150 else if (0 ==
151 strcmp ("list_indexed-context-dir", event->value.publish.cctx))
152 {
153 GNUNET_assert (0 == event->value.publish.completed);
154 GNUNET_assert (3 == event->value.publish.anonymity);
155 }
156 else
157 GNUNET_assert (0);
158 break;
159
160 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
161 if (0 == strcmp ("list_indexed-context-dir", event->value.publish.cctx))
162 {
163 GNUNET_assert (publish == event->value.publish.pc);
164 publish = NULL;
165 }
166 break;
167
168 default:
169 printf ("Unexpected event: %d\n", event->status);
170 break;
171 }
172 return ret;
173}
174
175
176static void
177run (void *cls,
178 const struct GNUNET_CONFIGURATION_Handle *cfg,
179 struct GNUNET_TESTING_Peer *peer)
180{
181 const char *keywords[] = {
182 "down_foo",
183 "down_bar",
184 };
185 char *buf;
186 struct GNUNET_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
254int
255main (int argc, char *argv[])
256{
257 if (0 != GNUNET_TESTING_peer_run ("test-fs-list-indexed",
258 "test_fs_list_indexed_data.conf",
259 &run, NULL))
260 return 1;
261 return 0;
262}
263
264
265/* end of test_fs_list_indexed.c */
diff --git a/src/fs/test_fs_list_indexed_data.conf b/src/fs/test_fs_list_indexed_data.conf
deleted file mode 100644
index 941809322..000000000
--- a/src/fs/test_fs_list_indexed_data.conf
+++ /dev/null
@@ -1,10 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-list-indexed/
4
5[transport]
6PLUGINS =
7
8[fs]
9ACTIVEMIGRATION = NO
10
diff --git a/src/fs/test_fs_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
39static int
40testMeta (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
151static int
152testMetaMore (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
185static int
186testMetaLink ()
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
221static int
222check ()
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
355static int
356test_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
401static int
402test_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
435static int
436test_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
466int
467main (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
32static struct GNUNET_CRYPTO_EcdsaPublicKey nsid;
33
34static struct GNUNET_FS_Uri *sks_expect_uri;
35
36static struct GNUNET_FS_Uri *ksk_expect_uri;
37
38static struct GNUNET_FS_Handle *fs;
39
40static struct GNUNET_FS_SearchContext *sks_search;
41
42static struct GNUNET_FS_SearchContext *ksk_search;
43
44static struct GNUNET_SCHEDULER_Task *kill_task;
45
46static int update_started;
47
48static int err;
49
50
51static void
52abort_ksk_search_task (void *cls)
53{
54 if (ksk_search != NULL)
55 {
56 GNUNET_FS_search_stop (ksk_search);
57 ksk_search = NULL;
58 if (sks_search == NULL)
59 {
60 GNUNET_FS_stop (fs);
61 if (NULL != kill_task)
62 GNUNET_SCHEDULER_cancel (kill_task);
63 }
64 }
65}
66
67
68static void
69abort_sks_search_task (void *cls)
70{
71 if (sks_search == NULL)
72 return;
73 GNUNET_FS_search_stop (sks_search);
74 sks_search = NULL;
75 if (ksk_search == NULL)
76 {
77 GNUNET_FS_stop (fs);
78 if (NULL != kill_task)
79 GNUNET_SCHEDULER_cancel (kill_task);
80 }
81}
82
83
84static void
85do_timeout (void *cls)
86{
87 err = 1;
88 fprintf (stderr, "%s", "Operation timed out\n");
89 kill_task = NULL;
90 abort_sks_search_task (NULL);
91 abort_ksk_search_task (NULL);
92}
93
94
95static void *
96progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
97{
98 switch (event->status)
99 {
100 case GNUNET_FS_STATUS_SEARCH_RESULT:
101 if (sks_search == event->value.search.sc)
102 {
103 if (! GNUNET_FS_uri_test_equal
104 (sks_expect_uri, event->value.search.specifics.result.uri))
105 {
106 fprintf (stderr, "%s", "Wrong result for sks search!\n");
107 err = 1;
108 }
109 /* give system 1ms to initiate update search! */
110 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
111 &abort_sks_search_task, NULL);
112 }
113 else if (ksk_search == event->value.search.sc)
114 {
115 if (! GNUNET_FS_uri_test_equal
116 (ksk_expect_uri, event->value.search.specifics.result.uri))
117 {
118 fprintf (stderr, "%s", "Wrong result for ksk search!\n");
119 err = 1;
120 }
121 GNUNET_SCHEDULER_add_now (&abort_ksk_search_task, NULL);
122 }
123 else
124 {
125 fprintf (stderr, "%s", "Unexpected search result received!\n");
126 GNUNET_break (0);
127 }
128 break;
129
130 case GNUNET_FS_STATUS_SEARCH_ERROR:
131 fprintf (stderr, "Error searching file: %s\n",
132 event->value.search.specifics.error.message);
133 if (sks_search == event->value.search.sc)
134 GNUNET_SCHEDULER_add_now (&abort_sks_search_task, NULL);
135 else if (ksk_search == event->value.search.sc)
136 GNUNET_SCHEDULER_add_now (&abort_ksk_search_task, NULL);
137 else
138 GNUNET_break (0);
139 break;
140
141 case GNUNET_FS_STATUS_SEARCH_START:
142 GNUNET_assert ((NULL == event->value.search.cctx) ||
143 (0 == strcmp ("sks_search", event->value.search.cctx)) ||
144 (0 == strcmp ("ksk_search", event->value.search.cctx)));
145 if (NULL == event->value.search.cctx)
146 {
147 GNUNET_assert (0 == strcmp ("sks_search", event->value.search.pctx));
148 update_started = GNUNET_YES;
149 }
150 GNUNET_assert (1 == event->value.search.anonymity);
151 break;
152
153 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
154 return NULL;
155
156 case GNUNET_FS_STATUS_SEARCH_STOPPED:
157 return NULL;
158
159 default:
160 fprintf (stderr, "Unexpected event: %d\n", event->status);
161 break;
162 }
163 return event->value.search.cctx;
164}
165
166
167static void
168publish_cont (void *cls, const struct GNUNET_FS_Uri *ksk_uri, const char *emsg)
169{
170 char *msg;
171 struct GNUNET_FS_Uri *sks_uri;
172 char sbuf[1024];
173 char buf[1024];
174 char *ret;
175
176 if (NULL != emsg)
177 {
178 fprintf (stderr, "Error publishing: %s\n", emsg);
179 err = 1;
180 GNUNET_FS_stop (fs);
181 return;
182 }
183 ret = GNUNET_STRINGS_data_to_string (&nsid, sizeof(nsid), buf, sizeof(buf));
184 GNUNET_assert (NULL != ret);
185 ret[0] = '\0';
186 GNUNET_snprintf (sbuf, sizeof(sbuf), "gnunet://fs/sks/%s/this", buf);
187 sks_uri = GNUNET_FS_uri_parse (sbuf, &msg);
188 if (NULL == sks_uri)
189 {
190 fprintf (stderr, "failed to parse URI `%s': %s\n", sbuf, msg);
191 err = 1;
192 GNUNET_FS_stop (fs);
193 GNUNET_free (msg);
194 return;
195 }
196 ksk_search =
197 GNUNET_FS_search_start (fs, ksk_uri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
198 "ksk_search");
199 sks_search =
200 GNUNET_FS_search_start (fs, sks_uri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
201 "sks_search");
202 GNUNET_FS_uri_destroy (sks_uri);
203}
204
205
206static void
207sks_cont (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg)
208{
209 struct GNUNET_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
238static void
239adv_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
269static void
270testNamespace (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
298static void
299run (void *cls,
300 const struct GNUNET_CONFIGURATION_Handle *cfg,
301 struct GNUNET_TESTING_Peer *peer)
302{
303 fs = GNUNET_FS_start (cfg, "test-fs-namespace", &progress_cb, NULL,
304 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
305 testNamespace ();
306}
307
308
309int
310main (int argc, char *argv[])
311{
312 if (0 != GNUNET_TESTING_peer_run ("test-fs-namespace",
313 "test_fs_namespace_data.conf",
314 &run, NULL))
315 return 1;
316 return err;
317}
318
319
320/* end of test_fs_namespace.c */
diff --git a/src/fs/test_fs_namespace_data.conf b/src/fs/test_fs_namespace_data.conf
deleted file mode 100644
index 70b954f7d..000000000
--- a/src/fs/test_fs_namespace_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-namespace/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_fs_namespace_list_updateable.c b/src/fs/test_fs_namespace_list_updateable.c
deleted file mode 100644
index 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
32static struct GNUNET_FS_Handle *fs;
33
34static int err;
35
36static struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
37
38static struct GNUNET_FS_MetaData *meta;
39
40static struct GNUNET_FS_Uri *uri_this;
41
42static struct GNUNET_FS_Uri *uri_next;
43
44static struct GNUNET_FS_BlockOptions bo;
45
46
47static void *
48progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
49{
50 return NULL;
51}
52
53
54static void
55do_shutdown ()
56{
57 if (uri_this != NULL)
58 GNUNET_FS_uri_destroy (uri_this);
59 if (uri_next != NULL)
60 GNUNET_FS_uri_destroy (uri_next);
61 if (meta != NULL)
62 GNUNET_FS_meta_data_destroy (meta);
63}
64
65
66static void
67check_next (void *cls, const char *last_id,
68 const struct GNUNET_FS_Uri *last_uri,
69 const struct GNUNET_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
78static void
79check_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
92static void
93sks_cont_next (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg)
94{
95 GNUNET_assert (NULL == emsg);
96 err += 2;
97 GNUNET_FS_namespace_list_updateable (fs, &ns, NULL, &check_this_next, NULL);
98}
99
100
101static void
102check_this (void *cls, const char *last_id,
103 const struct GNUNET_FS_Uri *last_uri,
104 const struct GNUNET_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
113static void
114sks_cont_this (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg)
115{
116 GNUNET_assert (NULL == emsg);
117 err = 1;
118 GNUNET_FS_namespace_list_updateable (fs, &ns, NULL, &check_this, NULL);
119 GNUNET_FS_publish_sks (fs,
120 &ns, "next", "future", meta, uri_next, &bo,
121 GNUNET_FS_PUBLISH_OPTION_NONE, &sks_cont_next, NULL);
122}
123
124
125static void
126testNamespace (void)
127{
128 GNUNET_CRYPTO_ecdsa_key_create (&ns);
129 bo.content_priority = 1;
130 bo.anonymity_level = 1;
131 bo.replication_level = 0;
132 bo.expiration_time =
133 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
134 meta = GNUNET_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
152static void
153run (void *cls,
154 const struct GNUNET_CONFIGURATION_Handle *cfg,
155 struct GNUNET_TESTING_Peer *peer)
156{
157 fs = GNUNET_FS_start (cfg, "test-fs-namespace", &progress_cb, NULL,
158 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
159 testNamespace ();
160}
161
162
163int
164main (int argc, char *argv[])
165{
166 if (0 != GNUNET_TESTING_peer_run ("test-fs-namespace-list-updateable",
167 "test_fs_namespace_data.conf",
168 &run, NULL))
169 return 1;
170 do_shutdown ();
171 return err;
172}
173
174
175/* end of test_fs_namespace_list_updateable.c */
diff --git a/src/fs/test_fs_publish.c b/src/fs/test_fs_publish.c
deleted file mode 100644
index 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
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static struct GNUNET_FS_PublishContext *publish;
52
53static char *fn1;
54
55static char *fn2;
56
57static int err;
58
59
60static void
61abort_publish_task (void *cls)
62{
63 GNUNET_FS_publish_stop (publish);
64 publish = NULL;
65 GNUNET_DISK_directory_remove (fn1);
66 GNUNET_free (fn1);
67 fn1 = NULL;
68 GNUNET_DISK_directory_remove (fn2);
69 GNUNET_free (fn2);
70 fn2 = NULL;
71}
72
73
74static void *
75progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
76{
77 void *ret;
78
79 ret = NULL;
80 switch (event->status)
81 {
82 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
83 ret = event->value.publish.cctx;
84 printf ("Publish complete, %llu kbps.\n",
85 (unsigned long long) (FILESIZE * 1000000LL
86 / (1
87 + GNUNET_TIME_absolute_get_duration
88 (start).rel_value_us) / 1024));
89 if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
90 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
91 break;
92
93 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
94 ret = event->value.publish.cctx;
95 GNUNET_assert (publish == event->value.publish.pc);
96 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
97 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
98 (unsigned long long) event->value.publish.completed,
99 (unsigned long long) event->value.publish.size,
100 event->value.publish.specifics.progress.depth,
101 (unsigned long long) event->value.publish.specifics.
102 progress.offset);
103 break;
104
105 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
106 ret = event->value.publish.cctx;
107 break;
108
109 case GNUNET_FS_STATUS_PUBLISH_ERROR:
110 ret = event->value.publish.cctx;
111 fprintf (stderr, "Error publishing file: %s\n",
112 event->value.publish.specifics.error.message);
113 err = 1;
114 if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
115 {
116 fprintf (stderr, "Scheduling abort task for error on `%s'\n",
117 (const char *) event->value.publish.cctx);
118 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
119 }
120 break;
121
122 case GNUNET_FS_STATUS_PUBLISH_START:
123 ret = event->value.publish.cctx;
124 if (0 == strcmp ("publish-context1", event->value.publish.cctx))
125 {
126 GNUNET_assert (0 ==
127 strcmp ("publish-context-dir", event->value.publish.pctx));
128 GNUNET_assert (FILESIZE == event->value.publish.size);
129 GNUNET_assert (0 == event->value.publish.completed);
130 GNUNET_assert (1 == event->value.publish.anonymity);
131 }
132 else if (0 == strcmp ("publish-context2", event->value.publish.cctx))
133 {
134 GNUNET_assert (0 ==
135 strcmp ("publish-context-dir", event->value.publish.pctx));
136 GNUNET_assert (FILESIZE == event->value.publish.size);
137 GNUNET_assert (0 == event->value.publish.completed);
138 GNUNET_assert (2 == event->value.publish.anonymity);
139 }
140 else if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
141 {
142 GNUNET_assert (0 == event->value.publish.completed);
143 GNUNET_assert (3 == event->value.publish.anonymity);
144 }
145 else
146 GNUNET_assert (0);
147 break;
148
149 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
150 if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
151 GNUNET_assert (publish == event->value.publish.pc);
152 break;
153
154 default:
155 printf ("Unexpected event: %d\n", event->status);
156 break;
157 }
158 return ret;
159}
160
161
162static void
163run (void *cls,
164 const struct GNUNET_CONFIGURATION_Handle *cfg,
165 struct GNUNET_TESTING_Peer *peer)
166{
167 const char *keywords[] = {
168 "down_foo",
169 "down_bar",
170 };
171 char *buf;
172 struct GNUNET_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
240int
241main (int argc, char *argv[])
242{
243 if (0 != GNUNET_TESTING_peer_run ("test-fs-publish",
244 "test_fs_publish_data.conf",
245 &run, NULL))
246 return 1;
247 return err;
248}
249
250
251/* end of test_fs_publish.c */
diff --git a/src/fs/test_fs_publish_data.conf b/src/fs/test_fs_publish_data.conf
deleted file mode 100644
index 0930cdfed..000000000
--- a/src/fs/test_fs_publish_data.conf
+++ /dev/null
@@ -1,10 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-publish/
4
5[transport]
6PLUGINS =
7
8[fs]
9ACTIVEMIGRATION = NO
10
diff --git a/src/fs/test_fs_publish_persistence.c b/src/fs/test_fs_publish_persistence.c
deleted file mode 100644
index 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
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static const struct GNUNET_CONFIGURATION_Handle *cfg;
52
53static struct GNUNET_FS_PublishContext *publish;
54
55static struct GNUNET_FS_PublishContext *publish;
56
57static char *fn1;
58
59static char *fn2;
60
61static int err;
62
63static struct GNUNET_SCHEDULER_Task *rtask;
64
65
66static void
67abort_publish_task (void *cls)
68{
69 GNUNET_FS_publish_stop (publish);
70 publish = NULL;
71 GNUNET_DISK_directory_remove (fn1);
72 GNUNET_free (fn1);
73 fn1 = NULL;
74 GNUNET_DISK_directory_remove (fn2);
75 GNUNET_free (fn2);
76 fn2 = NULL;
77 GNUNET_FS_stop (fs);
78 fs = NULL;
79 if (NULL != rtask)
80 {
81 GNUNET_SCHEDULER_cancel (rtask);
82 rtask = NULL;
83 }
84}
85
86
87static void *
88progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event);
89
90
91static void
92restart_fs_task (void *cls)
93{
94 rtask = NULL;
95 GNUNET_FS_stop (fs);
96 fs = GNUNET_FS_start (cfg, "test-fs-publish-persistence",
97 &progress_cb, NULL,
98 GNUNET_FS_FLAGS_PERSISTENCE,
99 GNUNET_FS_OPTIONS_END);
100}
101
102
103/**
104 * Consider scheduling the restart-task.
105 * Only runs the restart task once per event
106 * category.
107 *
108 * @param ev type of the event to consider
109 */
110static void
111consider_restart (int ev)
112{
113 static int prev[32];
114 static int off;
115 int i;
116
117 for (i = 0; i < off; i++)
118 if (prev[i] == ev)
119 return;
120 prev[off++] = ev;
121 rtask =
122 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_URGENT,
123 &restart_fs_task, NULL);
124}
125
126
127static void *
128progress_cb (void *cls,
129 const struct GNUNET_FS_ProgressInfo *event)
130{
131 void *ret;
132
133 ret = NULL;
134 switch (event->status)
135 {
136 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
137 ret = event->value.publish.cctx;
138 printf ("Publish complete, %llu kbps.\n",
139 (unsigned long long) (FILESIZE * 1000000LL
140 / (1
141 + GNUNET_TIME_absolute_get_duration
142 (start).rel_value_us) / 1024));
143 if ((NULL != event->value.publish.cctx) &&
144 (0 == strcmp ("publish-context-dir", event->value.publish.cctx)))
145 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
146 break;
147
148 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
149 ret = event->value.publish.cctx;
150 return ret;
151
152 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
153 consider_restart (event->status);
154 ret = event->value.publish.cctx;
155 GNUNET_assert (publish == event->value.publish.pc);
156#if VERBOSE
157 printf ("Publish is progressing (%llu/%llu at level %u off %llu)...\n",
158 (unsigned long long) event->value.publish.completed,
159 (unsigned long long) event->value.publish.size,
160 event->value.publish.specifics.progress.depth,
161 (unsigned long long) event->value.publish.specifics.
162 progress.offset);
163#endif
164 break;
165
166 case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
167 if (event->value.publish.pc == publish)
168 publish = NULL;
169 break;
170
171 case GNUNET_FS_STATUS_PUBLISH_RESUME:
172 if (NULL == publish)
173 {
174 GNUNET_assert (GNUNET_YES ==
175 GNUNET_FS_file_information_is_directory (event->
176 value.publish.
177 fi));
178 publish = event->value.publish.pc;
179 return "publish-context-dir";
180 }
181 break;
182
183 case GNUNET_FS_STATUS_PUBLISH_ERROR:
184 ret = event->value.publish.cctx;
185 fprintf (stderr, "Error publishing file: %s\n",
186 event->value.publish.specifics.error.message);
187 err = 1;
188 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
189 break;
190
191 case GNUNET_FS_STATUS_PUBLISH_START:
192 consider_restart (event->status);
193 publish = event->value.publish.pc;
194 ret = event->value.publish.cctx;
195 if (0 == strcmp ("publish-context1", event->value.publish.cctx))
196 {
197 GNUNET_assert (0 ==
198 strcmp ("publish-context-dir", event->value.publish.pctx));
199 GNUNET_assert (FILESIZE == event->value.publish.size);
200 GNUNET_assert (0 == event->value.publish.completed);
201 GNUNET_assert (1 == event->value.publish.anonymity);
202 }
203 else if (0 == strcmp ("publish-context2", event->value.publish.cctx))
204 {
205 GNUNET_assert (0 ==
206 strcmp ("publish-context-dir", event->value.publish.pctx));
207 GNUNET_assert (FILESIZE == event->value.publish.size);
208 GNUNET_assert (0 == event->value.publish.completed);
209 GNUNET_assert (2 == event->value.publish.anonymity);
210 }
211 else if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
212 {
213 GNUNET_assert (0 == event->value.publish.completed);
214 GNUNET_assert (3 == event->value.publish.anonymity);
215 }
216 else
217 GNUNET_assert (0);
218 break;
219
220 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
221 consider_restart (event->status);
222 if ((NULL != event->value.publish.cctx) &&
223 (0 == strcmp ("publish-context-dir", event->value.publish.cctx)))
224 GNUNET_assert (publish == event->value.publish.pc);
225 break;
226
227 default:
228 printf ("Unexpected event: %d\n", event->status);
229 break;
230 }
231 return ret;
232}
233
234
235static void
236run (void *cls,
237 const struct GNUNET_CONFIGURATION_Handle *c,
238 struct GNUNET_TESTING_Peer *peer)
239{
240 const char *keywords[] = {
241 "down_foo",
242 "down_bar",
243 };
244 char *buf;
245 struct GNUNET_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
311int
312main (int argc, char *argv[])
313{
314 if (0 != GNUNET_TESTING_peer_run ("test-fs-publish-persistence",
315 "test_fs_publish_data.conf",
316 &run, NULL))
317 return 1;
318 return err;
319}
320
321
322/* end of test_fs_publish_persistence.c */
diff --git a/src/fs/test_fs_search.c b/src/fs/test_fs_search.c
deleted file mode 100644
index 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
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static struct GNUNET_FS_SearchContext *search;
52
53static struct GNUNET_FS_PublishContext *publish;
54
55static struct GNUNET_SCHEDULER_Task *timeout_task;
56
57static int err;
58
59
60static void
61abort_publish_task (void *cls)
62{
63 if (NULL != publish)
64 {
65 GNUNET_FS_publish_stop (publish);
66 publish = NULL;
67 }
68 if (NULL != timeout_task)
69 {
70 GNUNET_SCHEDULER_cancel (timeout_task);
71 timeout_task = NULL;
72 }
73}
74
75
76static void
77abort_error (void *cls)
78{
79 fprintf (stderr,
80 "Timeout\n");
81 timeout_task = NULL;
82 if (NULL != search)
83 {
84 GNUNET_FS_search_stop (search);
85 search = NULL;
86 }
87 if (NULL != publish)
88 {
89 GNUNET_FS_publish_stop (publish);
90 publish = NULL;
91 }
92 err = 1;
93}
94
95
96static void
97abort_search_task (void *cls)
98{
99 if (NULL != search)
100 {
101 GNUNET_FS_search_stop (search);
102 search = NULL;
103 }
104}
105
106
107static void *
108progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
109{
110 const char *keywords[] = {
111 "down_foo"
112 };
113 struct GNUNET_FS_Uri *kuri;
114
115 switch (event->status)
116 {
117 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
118 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
119 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
120 (unsigned long long) event->value.publish.completed,
121 (unsigned long long) event->value.publish.size,
122 event->value.publish.specifics.progress.depth,
123 (unsigned long long) event->value.publish.specifics.
124 progress.offset);
125 break;
126
127 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
128 break;
129
130 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
131 kuri = GNUNET_FS_uri_ksk_create_from_args (1, keywords);
132 start = GNUNET_TIME_absolute_get ();
133 search =
134 GNUNET_FS_search_start (fs, kuri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
135 "search");
136 GNUNET_FS_uri_destroy (kuri);
137 GNUNET_assert (search != NULL);
138 break;
139
140 case GNUNET_FS_STATUS_SEARCH_RESULT:
141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
142 "Search complete.\n");
143 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
144 break;
145
146 case GNUNET_FS_STATUS_PUBLISH_ERROR:
147 fprintf (stderr, "Error publishing file: %s\n",
148 event->value.publish.specifics.error.message);
149 GNUNET_break (0);
150 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
151 break;
152
153 case GNUNET_FS_STATUS_SEARCH_ERROR:
154 fprintf (stderr, "Error searching file: %s\n",
155 event->value.search.specifics.error.message);
156 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
157 break;
158
159 case GNUNET_FS_STATUS_PUBLISH_START:
160 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
161 GNUNET_assert (NULL == event->value.publish.pctx);
162 GNUNET_assert (FILESIZE == event->value.publish.size);
163 GNUNET_assert (0 == event->value.publish.completed);
164 GNUNET_assert (1 == event->value.publish.anonymity);
165 break;
166
167 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
168 GNUNET_assert (publish == event->value.publish.pc);
169 GNUNET_assert (FILESIZE == event->value.publish.size);
170 GNUNET_assert (1 == event->value.publish.anonymity);
171 GNUNET_FS_stop (fs);
172 fs = NULL;
173 break;
174
175 case GNUNET_FS_STATUS_SEARCH_START:
176 GNUNET_assert (search == NULL);
177 GNUNET_assert (0 == strcmp ("search", event->value.search.cctx));
178 GNUNET_assert (1 == event->value.search.anonymity);
179 break;
180
181 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
182 break;
183
184 case GNUNET_FS_STATUS_SEARCH_STOPPED:
185 GNUNET_assert (search == event->value.search.sc);
186 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
187 break;
188
189 default:
190 fprintf (stderr, "Unexpected event: %d\n", event->status);
191 break;
192 }
193 return NULL;
194}
195
196
197static void
198run (void *cls,
199 const struct GNUNET_CONFIGURATION_Handle *cfg,
200 struct GNUNET_TESTING_Peer *peer)
201{
202 const char *keywords[] = {
203 "down_foo",
204 "down_bar"
205 };
206 char *buf;
207 struct GNUNET_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
241int
242main (int argc, char *argv[])
243{
244 if (0 != GNUNET_TESTING_peer_run ("test-fs-search",
245 "test_fs_search_data.conf",
246 &run, NULL))
247 return 1;
248 return err;
249}
250
251
252/* end of test_fs_search.c */
diff --git a/src/fs/test_fs_search_data.conf b/src/fs/test_fs_search_data.conf
deleted file mode 100644
index 8b24e328d..000000000
--- a/src/fs/test_fs_search_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-search/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_fs_search_persistence.c b/src/fs/test_fs_search_persistence.c
deleted file mode 100644
index 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
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static struct GNUNET_FS_SearchContext *search;
52
53static struct GNUNET_FS_PublishContext *publish;
54
55static const struct GNUNET_CONFIGURATION_Handle *cfg;
56
57static struct GNUNET_SCHEDULER_Task *timeout_task;
58
59static int err;
60
61
62static void
63abort_error (void *cls)
64{
65 timeout_task = NULL;
66 fprintf (stderr,
67 "Timeout\n");
68 if (NULL != search)
69 {
70 GNUNET_FS_search_stop (search);
71 search = NULL;
72 }
73 if (NULL != publish)
74 {
75 GNUNET_FS_publish_stop (publish);
76 publish = NULL;
77 }
78 err = 1;
79}
80
81
82static void
83abort_publish_task (void *cls)
84{
85 if (NULL != publish)
86 {
87 GNUNET_FS_publish_stop (publish);
88 publish = NULL;
89 }
90 if (NULL != timeout_task)
91 {
92 GNUNET_SCHEDULER_cancel (timeout_task);
93 timeout_task = NULL;
94 }
95}
96
97
98static void
99abort_search_task (void *cls)
100{
101 if (NULL != search)
102 {
103 GNUNET_FS_search_stop (search);
104 search = NULL;
105 }
106}
107
108
109static void *
110progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event);
111
112
113static void
114restart_fs_task (void *cls)
115{
116 GNUNET_FS_stop (fs);
117 fs = GNUNET_FS_start (cfg, "test-fs-search-persistence", &progress_cb, NULL,
118 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
119}
120
121
122/**
123 * Consider scheduling the restart-task.
124 * Only runs the restart task once per event
125 * category.
126 *
127 * @param ev type of the event to consider
128 */
129static void
130consider_restart (int ev)
131{
132 static int prev[32];
133 static int off;
134 int i;
135
136 for (i = 0; i < off; i++)
137 if (prev[i] == ev)
138 return;
139 prev[off++] = ev;
140 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_URGENT,
141 &restart_fs_task, NULL);
142}
143
144
145static void *
146progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
147{
148 const char *keywords[] = {
149 "down_foo"
150 };
151 struct GNUNET_FS_Uri *kuri;
152
153 switch (event->status)
154 {
155 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
157 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
158 (unsigned long long) event->value.publish.completed,
159 (unsigned long long) event->value.publish.size,
160 event->value.publish.specifics.progress.depth,
161 (unsigned long long) event->value.publish.specifics.
162 progress.offset);
163 break;
164
165 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
166 break;
167
168 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
169 kuri = GNUNET_FS_uri_ksk_create_from_args (1, keywords);
170 start = GNUNET_TIME_absolute_get ();
171 GNUNET_FS_search_start (fs, kuri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
172 "search");
173 GNUNET_FS_uri_destroy (kuri);
174 GNUNET_assert (search != NULL);
175 break;
176
177 case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
178 if (event->value.publish.pc == publish)
179 publish = NULL;
180 break;
181
182 case GNUNET_FS_STATUS_PUBLISH_RESUME:
183 if (NULL == publish)
184 publish = event->value.publish.pc;
185 break;
186
187 case GNUNET_FS_STATUS_SEARCH_RESULT:
188 /* FIXME: consider_restart (event->status); cannot be tested with
189 * search result since we exit here after the first one... */
190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191 "Search complete.\n");
192 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
193 break;
194
195 case GNUNET_FS_STATUS_PUBLISH_ERROR:
196 fprintf (stderr, "Error publishing file: %s\n",
197 event->value.publish.specifics.error.message);
198 GNUNET_break (0);
199 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
200 break;
201
202 case GNUNET_FS_STATUS_SEARCH_ERROR:
203 fprintf (stderr, "Error searching file: %s\n",
204 event->value.search.specifics.error.message);
205 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
206 break;
207
208 case GNUNET_FS_STATUS_SEARCH_SUSPEND:
209 if (event->value.search.sc == search)
210 search = NULL;
211 break;
212
213 case GNUNET_FS_STATUS_SEARCH_RESUME:
214 if (NULL == search)
215 {
216 search = event->value.search.sc;
217 return "search";
218 }
219 break;
220
221 case GNUNET_FS_STATUS_PUBLISH_START:
222 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
223 GNUNET_assert (NULL == event->value.publish.pctx);
224 GNUNET_assert (FILESIZE == event->value.publish.size);
225 GNUNET_assert (0 == event->value.publish.completed);
226 GNUNET_assert (1 == event->value.publish.anonymity);
227 break;
228
229 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
230 GNUNET_assert (publish == event->value.publish.pc);
231 GNUNET_assert (FILESIZE == event->value.publish.size);
232 GNUNET_assert (1 == event->value.publish.anonymity);
233 GNUNET_FS_stop (fs);
234 fs = NULL;
235 break;
236
237 case GNUNET_FS_STATUS_SEARCH_START:
238 consider_restart (event->status);
239 GNUNET_assert (search == NULL);
240 search = event->value.search.sc;
241 GNUNET_assert (0 == strcmp ("search", event->value.search.cctx));
242 GNUNET_assert (1 == event->value.search.anonymity);
243 break;
244
245 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
246 break;
247
248 case GNUNET_FS_STATUS_SEARCH_STOPPED:
249 GNUNET_assert (search == event->value.search.sc);
250 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
251 search = NULL;
252 break;
253
254 default:
255 fprintf (stderr, "Unexpected event: %d\n", event->status);
256 break;
257 }
258 return NULL;
259}
260
261
262static void
263run (void *cls,
264 const struct GNUNET_CONFIGURATION_Handle *c,
265 struct GNUNET_TESTING_Peer *peer)
266{
267 const char *keywords[] = {
268 "down_foo",
269 "down_bar"
270 };
271 char *buf;
272 struct GNUNET_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
307int
308main (int argc, char *argv[])
309{
310 if (0 != GNUNET_TESTING_peer_run ("test-fs-search-persistence",
311 "test_fs_search_data.conf",
312 &run, NULL))
313 return 1;
314 return err;
315}
316
317
318/* end of test_fs_search_persistence.c */
diff --git a/src/fs/test_fs_search_probes.c b/src/fs/test_fs_search_probes.c
deleted file mode 100644
index 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
48static struct GNUNET_TIME_Absolute start;
49
50static struct GNUNET_FS_Handle *fs;
51
52static struct GNUNET_FS_SearchContext *search;
53
54static struct GNUNET_FS_PublishContext *publish;
55
56static struct GNUNET_SCHEDULER_Task *timeout_task;
57
58static int err;
59
60
61static void
62abort_error (void *cls)
63{
64 timeout_task = NULL;
65 fprintf (stderr,
66 "Timeout\n");
67 if (NULL != search)
68 {
69 GNUNET_FS_search_stop (search);
70 search = NULL;
71 }
72 if (NULL != publish)
73 {
74 GNUNET_FS_publish_stop (publish);
75 publish = NULL;
76 }
77 err = 1;
78}
79
80
81static void
82abort_publish_task (void *cls)
83{
84 if (NULL != publish)
85 {
86 GNUNET_FS_publish_stop (publish);
87 publish = NULL;
88 }
89 if (NULL != timeout_task)
90 {
91 GNUNET_SCHEDULER_cancel (timeout_task);
92 timeout_task = NULL;
93 }
94}
95
96
97static void
98abort_search_task (void *cls)
99{
100 if (search != NULL)
101 GNUNET_FS_search_stop (search);
102 search = NULL;
103}
104
105
106static void *
107progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
108{
109 const char *keywords[] = {
110 "down_foo"
111 };
112 struct GNUNET_FS_Uri *kuri;
113
114 switch (event->status)
115 {
116 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
117 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
118 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
119 (unsigned long long) event->value.publish.completed,
120 (unsigned long long) event->value.publish.size,
121 event->value.publish.specifics.progress.depth,
122 (unsigned long long) event->value.publish.specifics.
123 progress.offset);
124 break;
125
126 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
127 break;
128
129 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
130 kuri = GNUNET_FS_uri_ksk_create_from_args (1, keywords);
131 start = GNUNET_TIME_absolute_get ();
132 search =
133 GNUNET_FS_search_start (fs, kuri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
134 "search");
135 GNUNET_FS_uri_destroy (kuri);
136 GNUNET_assert (search != NULL);
137 break;
138
139 case GNUNET_FS_STATUS_SEARCH_RESULT:
140 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Search complete.\n");
141 break;
142
143 case GNUNET_FS_STATUS_PUBLISH_ERROR:
144 fprintf (stderr, "Error publishing file: %s\n",
145 event->value.publish.specifics.error.message);
146 GNUNET_break (0);
147 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
148 break;
149
150 case GNUNET_FS_STATUS_SEARCH_ERROR:
151 fprintf (stderr, "Error searching file: %s\n",
152 event->value.search.specifics.error.message);
153 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
154 break;
155
156 case GNUNET_FS_STATUS_PUBLISH_START:
157 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
158 GNUNET_assert (NULL == event->value.publish.pctx);
159 GNUNET_assert (FILESIZE == event->value.publish.size);
160 GNUNET_assert (0 == event->value.publish.completed);
161 GNUNET_assert (1 == event->value.publish.anonymity);
162 break;
163
164 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
165 GNUNET_assert (publish == event->value.publish.pc);
166 GNUNET_assert (FILESIZE == event->value.publish.size);
167 GNUNET_assert (1 == event->value.publish.anonymity);
168 GNUNET_FS_stop (fs);
169 fs = NULL;
170 break;
171
172 case GNUNET_FS_STATUS_SEARCH_UPDATE:
173 if ((0 < event->value.search.specifics.update.availability_rank) &&
174 (0 < event->value.search.specifics.update.availability_certainty))
175 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
176 break;
177
178 case GNUNET_FS_STATUS_SEARCH_START:
179 GNUNET_assert (search == NULL);
180 GNUNET_assert (0 == strcmp ("search", event->value.search.cctx));
181 GNUNET_assert (1 == event->value.search.anonymity);
182 break;
183
184 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
185 break;
186
187 case GNUNET_FS_STATUS_SEARCH_STOPPED:
188 GNUNET_assert (search == event->value.search.sc);
189 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
190 break;
191
192 default:
193 fprintf (stderr,
194 "Unexpected event: %d\n",
195 event->status);
196 break;
197 }
198 return NULL;
199}
200
201
202static void
203run (void *cls,
204 const struct GNUNET_CONFIGURATION_Handle *cfg,
205 struct GNUNET_TESTING_Peer *peer)
206{
207 const char *keywords[] = {
208 "down_foo",
209 "down_bar"
210 };
211 char *buf;
212 struct GNUNET_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
247int
248main (int argc, char *argv[])
249{
250 if (0 != GNUNET_TESTING_peer_run ("test-fs-search-probes",
251 "test_fs_search_data.conf",
252 &run, NULL))
253 return 1;
254 return err;
255}
256
257
258/* end of test_fs_search_probes.c */
diff --git a/src/fs/test_fs_search_with_and.c b/src/fs/test_fs_search_with_and.c
deleted file mode 100644
index 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
53static struct GNUNET_TIME_Absolute start;
54
55static struct GNUNET_FS_Handle *fs;
56
57static struct GNUNET_FS_SearchContext *search;
58
59static struct GNUNET_FS_PublishContext *publish;
60
61static struct GNUNET_SCHEDULER_Task *timeout_task;
62
63static int err;
64
65static int processed_files;
66
67
68static void
69abort_publish_task (void *cls)
70{
71 if (NULL != publish)
72 {
73 GNUNET_FS_publish_stop (publish);
74 publish = NULL;
75 }
76 if (NULL != timeout_task)
77 {
78 GNUNET_SCHEDULER_cancel (timeout_task);
79 timeout_task = NULL;
80 }
81}
82
83
84static void
85abort_error (void *cls)
86{
87 fprintf (stderr,
88 "Timeout\n");
89 timeout_task = NULL;
90 if (NULL != search)
91 {
92 GNUNET_FS_search_stop (search);
93 search = NULL;
94 }
95 if (NULL != publish)
96 {
97 GNUNET_FS_publish_stop (publish);
98 publish = NULL;
99 }
100 err = 1;
101}
102
103
104static void
105abort_search_task (void *cls)
106{
107 if (NULL != search)
108 {
109 GNUNET_FS_search_stop (search);
110 search = NULL;
111 }
112}
113
114
115static void *
116progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
117{
118 struct GNUNET_FS_Uri *kuri;
119
120 switch (event->status)
121 {
122 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
123 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
124 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
125 (unsigned long long) event->value.publish.completed,
126 (unsigned long long) event->value.publish.size,
127 event->value.publish.specifics.progress.depth,
128 (unsigned long long) event->value.publish.specifics.
129 progress.offset);
130 break;
131
132 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
133 break;
134
135 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
136 processed_files++;
137 if (processed_files == NUM_FILES)
138 {
139 char *emsg = NULL;
140 kuri = GNUNET_FS_uri_ksk_create ("+down_foo +down_bar", &emsg);
141 GNUNET_assert (kuri != NULL);
142
143 start = GNUNET_TIME_absolute_get ();
144 search =
145 GNUNET_FS_search_start (fs, kuri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
146 "search");
147 GNUNET_FS_uri_destroy (kuri);
148 GNUNET_assert (search != NULL);
149 }
150 break;
151
152 case GNUNET_FS_STATUS_SEARCH_RESULT:
153 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
154 "Search complete.\n");
155 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
156 break;
157
158 case GNUNET_FS_STATUS_PUBLISH_ERROR:
159 fprintf (stderr, "Error publishing file: %s\n",
160 event->value.publish.specifics.error.message);
161 GNUNET_break (0);
162 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
163 break;
164
165 case GNUNET_FS_STATUS_SEARCH_ERROR:
166 fprintf (stderr, "Error searching file: %s\n",
167 event->value.search.specifics.error.message);
168 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
169 break;
170
171 case GNUNET_FS_STATUS_PUBLISH_START:
172 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
173 GNUNET_assert (NULL == event->value.publish.pctx);
174 GNUNET_assert (FILESIZE == event->value.publish.size);
175 GNUNET_assert (0 == event->value.publish.completed);
176 GNUNET_assert (1 == event->value.publish.anonymity);
177 break;
178
179 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
180 GNUNET_assert (publish == event->value.publish.pc);
181 GNUNET_assert (FILESIZE == event->value.publish.size);
182 GNUNET_assert (1 == event->value.publish.anonymity);
183 GNUNET_FS_stop (fs);
184 fs = NULL;
185 break;
186
187 case GNUNET_FS_STATUS_SEARCH_START:
188 GNUNET_assert (search == NULL);
189 GNUNET_assert (0 == strcmp ("search", event->value.search.cctx));
190 GNUNET_assert (1 == event->value.search.anonymity);
191 break;
192
193 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
194 break;
195
196 case GNUNET_FS_STATUS_SEARCH_STOPPED:
197 GNUNET_assert (search == event->value.search.sc);
198 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
199 break;
200
201 default:
202 fprintf (stderr, "Unexpected event: %d\n", event->status);
203 break;
204 }
205 return NULL;
206}
207
208
209static void
210run (void *cls,
211 const struct GNUNET_CONFIGURATION_Handle *cfg,
212 struct GNUNET_TESTING_Peer *peer)
213{
214 const char *keywords[] = {
215 "down_foo",
216 "down_bar"
217 };
218 char *buf;
219 struct GNUNET_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
261int
262main (int argc, char *argv[])
263{
264 if (0 != GNUNET_TESTING_peer_run ("test-fs-search-with-and",
265 "test_fs_search_data.conf",
266 &run, NULL))
267 return 1;
268 return err;
269}
270
271
272/* end of test_fs_search.c */
diff --git a/src/fs/test_fs_start_stop.c b/src/fs/test_fs_start_stop.c
deleted file mode 100644
index 509fbe76a..000000000
--- a/src/fs/test_fs_start_stop.c
+++ /dev/null
@@ -1,64 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_start_stop.c
22 * @brief testcase for fs.c (start-stop only)
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_fs_service.h"
30
31
32static void *
33progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
34{
35 return NULL;
36}
37
38
39static void
40run (void *cls,
41 const struct GNUNET_CONFIGURATION_Handle *cfg,
42 struct GNUNET_TESTING_Peer *peer)
43{
44 struct GNUNET_FS_Handle *fs;
45
46 fs = GNUNET_FS_start (cfg, "test-fs-start-stop", &progress_cb, NULL,
47 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
48 GNUNET_assert (NULL != fs);
49 GNUNET_FS_stop (fs);
50}
51
52
53int
54main (int argc, char *argv[])
55{
56 if (0 != GNUNET_TESTING_peer_run ("test-fs-start-stop",
57 "test_fs_data.conf",
58 &run, NULL))
59 return 1;
60 return 0;
61}
62
63
64/* end of test_fs_start_stop.c */
diff --git a/src/fs/test_fs_test_lib.c b/src/fs/test_fs_test_lib.c
deleted file mode 100644
index 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
45static struct GNUNET_TESTBED_Peer *the_peers[NUM_DAEMONS];
46
47static struct GNUNET_TIME_Absolute start_time;
48
49static int ret;
50
51
52static void
53do_stop (void *cls)
54{
55 char *fn = cls;
56
57 if (0 ==
58 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
59 TIMEOUT)).
60 rel_value_us)
61 {
62 GNUNET_break (0);
63 ret = 1;
64 }
65 else
66 {
67 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
68 "Finished download, shutting down\n");
69 }
70 if (NULL != fn)
71 {
72 GNUNET_DISK_directory_remove (fn);
73 GNUNET_free (fn);
74 }
75 GNUNET_SCHEDULER_shutdown ();
76}
77
78
79static void
80do_download (void *cls,
81 const struct GNUNET_FS_Uri *uri,
82 const char *fn)
83{
84 if (NULL == uri)
85 {
86 GNUNET_break (0);
87 GNUNET_SCHEDULER_shutdown ();
88 ret = 1;
89 return;
90 }
91 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
92 "Downloading %llu bytes\n",
93 (unsigned long long) FILESIZE);
94 start_time = GNUNET_TIME_absolute_get ();
95 GNUNET_FS_TEST_download (the_peers[0],
96 TIMEOUT, 1, SEED,
97 uri,
98 VERBOSE,
99 &do_stop,
100 (NULL == fn) ? NULL : GNUNET_strdup (fn));
101}
102
103
104static void
105do_publish (void *cls,
106 struct GNUNET_TESTBED_Operation *op,
107 const char *emsg)
108{
109 GNUNET_TESTBED_operation_done (op);
110 if (NULL != emsg)
111 {
112 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to connect peers: %s\n", emsg);
113 GNUNET_break (0);
114 ret = 1;
115 GNUNET_SCHEDULER_shutdown ();
116 return;
117 }
118 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
119 (unsigned long long) FILESIZE);
120 GNUNET_FS_TEST_publish (the_peers[0], TIMEOUT, 1, GNUNET_NO, FILESIZE, SEED,
121 VERBOSE, &do_download, NULL);
122}
123
124
125/**
126 * Actual main function for the test.
127 *
128 * @param cls closure
129 * @param h the run handle
130 * @param num_peers number of peers in 'peers'
131 * @param peers handle to peers run in the testbed
132 * @param links_succeeded the number of overlay link connection attempts that
133 * succeeded
134 * @param links_failed the number of overlay link connection attempts that
135 * failed
136 */
137static void
138run (void *cls,
139 struct GNUNET_TESTBED_RunHandle *h,
140 unsigned int num_peers,
141 struct GNUNET_TESTBED_Peer **peers,
142 unsigned int links_succeeded,
143 unsigned int links_failed)
144{
145 unsigned int i;
146
147 GNUNET_assert (NUM_DAEMONS == num_peers);
148 for (i = 0; i < num_peers; i++)
149 the_peers[i] = peers[i];
150 GNUNET_TESTBED_overlay_connect (NULL,
151 &do_publish,
152 NULL,
153 peers[0],
154 peers[1]);
155}
156
157
158/**
159 * Main function that initializes the testbed.
160 *
161 * @param argc ignored
162 * @param argv ignored
163 * @return 0 on success
164 */
165int
166main (int argc, char *argv[])
167{
168 GNUNET_DISK_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
48static struct GNUNET_TIME_Absolute start;
49
50static struct GNUNET_FS_Handle *fs;
51
52static struct GNUNET_FS_UnindexContext *unindex;
53
54static struct GNUNET_FS_PublishContext *publish;
55
56static char *fn;
57
58
59static void
60abort_publish_task (void *cls)
61{
62 GNUNET_FS_publish_stop (publish);
63 publish = NULL;
64}
65
66
67static void
68abort_unindex_task (void *cls)
69{
70 GNUNET_FS_unindex_stop (unindex);
71 unindex = NULL;
72 GNUNET_DISK_directory_remove (fn);
73 GNUNET_free (fn);
74 fn = NULL;
75}
76
77
78static void *
79progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
80{
81 switch (event->status)
82 {
83 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
84 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
85 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
86 (unsigned long long) event->value.publish.completed,
87 (unsigned long long) event->value.publish.size,
88 event->value.publish.specifics.progress.depth,
89 (unsigned long long) event->value.publish.specifics.
90 progress.offset);
91 break;
92
93 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
94 break;
95
96 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
97 printf ("Publishing complete, %llu kbps.\n",
98 (unsigned long long) (FILESIZE * 1000000LL
99 / (1
100 + GNUNET_TIME_absolute_get_duration
101 (start).rel_value_us) / 1024));
102 start = GNUNET_TIME_absolute_get ();
103 unindex = GNUNET_FS_unindex_start (fs, fn, "unindex");
104 GNUNET_assert (unindex != NULL);
105 break;
106
107 case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
108 printf ("Unindex complete, %llu kbps.\n",
109 (unsigned long long) (FILESIZE * 1000000LL
110 / (1
111 + GNUNET_TIME_absolute_get_duration
112 (start).rel_value_us) / 1024));
113 GNUNET_SCHEDULER_add_now (&abort_unindex_task, NULL);
114 break;
115
116 case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
117 GNUNET_assert (unindex == event->value.unindex.uc);
118 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
119 "Unindex is progressing (%llu/%llu at level %u off %llu)...\n",
120 (unsigned long long) event->value.unindex.completed,
121 (unsigned long long) event->value.unindex.size,
122 event->value.unindex.specifics.progress.depth,
123 (unsigned long long) event->value.unindex.specifics.
124 progress.offset);
125 break;
126
127 case GNUNET_FS_STATUS_PUBLISH_ERROR:
128 fprintf (stderr, "Error publishing file: %s\n",
129 event->value.publish.specifics.error.message);
130 GNUNET_break (0);
131 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
132 break;
133
134 case GNUNET_FS_STATUS_UNINDEX_ERROR:
135 fprintf (stderr, "Error unindexing file: %s\n",
136 event->value.unindex.specifics.error.message);
137 GNUNET_SCHEDULER_add_now (&abort_unindex_task, NULL);
138 break;
139
140 case GNUNET_FS_STATUS_PUBLISH_START:
141 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
142 GNUNET_assert (NULL == event->value.publish.pctx);
143 GNUNET_assert (FILESIZE == event->value.publish.size);
144 GNUNET_assert (0 == event->value.publish.completed);
145 GNUNET_assert (1 == event->value.publish.anonymity);
146 break;
147
148 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
149 GNUNET_assert (publish == event->value.publish.pc);
150 GNUNET_assert (FILESIZE == event->value.publish.size);
151 GNUNET_assert (1 == event->value.publish.anonymity);
152 GNUNET_FS_stop (fs);
153 fs = NULL;
154 break;
155
156 case GNUNET_FS_STATUS_UNINDEX_START:
157 GNUNET_assert (unindex == NULL);
158 GNUNET_assert (0 == strcmp ("unindex", event->value.unindex.cctx));
159 GNUNET_assert (0 == strcmp (fn, event->value.unindex.filename));
160 GNUNET_assert (FILESIZE == event->value.unindex.size);
161 GNUNET_assert (0 == event->value.unindex.completed);
162 break;
163
164 case GNUNET_FS_STATUS_UNINDEX_STOPPED:
165 GNUNET_assert (unindex == event->value.unindex.uc);
166 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
167 break;
168
169 default:
170 printf ("Unexpected event: %d\n", event->status);
171 break;
172 }
173 return NULL;
174}
175
176
177static void
178run (void *cls,
179 const struct GNUNET_CONFIGURATION_Handle *cfg,
180 struct GNUNET_TESTING_Peer *peer)
181{
182 const char *keywords[] = {
183 "down_foo",
184 "down_bar",
185 };
186 char *buf;
187 struct GNUNET_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
226int
227main (int argc, char *argv[])
228{
229 if (0 != GNUNET_TESTING_peer_run ("test-fs-unindex",
230 "test_fs_unindex_data.conf",
231 &run, NULL))
232 return 1;
233 return 0;
234}
235
236
237/* end of test_fs_unindex.c */
diff --git a/src/fs/test_fs_unindex_data.conf b/src/fs/test_fs_unindex_data.conf
deleted file mode 100644
index dde401857..000000000
--- a/src/fs/test_fs_unindex_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-unindex/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_fs_unindex_persistence.c b/src/fs/test_fs_unindex_persistence.c
deleted file mode 100644
index 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
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static struct GNUNET_FS_UnindexContext *unindex;
52
53static struct GNUNET_FS_PublishContext *publish;
54
55static char *fn;
56
57static const struct GNUNET_CONFIGURATION_Handle *cfg;
58
59
60static void
61abort_publish_task (void *cls)
62{
63 GNUNET_FS_publish_stop (publish);
64 publish = NULL;
65}
66
67
68static void
69abort_unindex_task (void *cls)
70{
71 if (unindex != NULL)
72 {
73 GNUNET_FS_unindex_stop (unindex);
74 unindex = NULL;
75 }
76 if (fn != NULL)
77 {
78 GNUNET_DISK_directory_remove (fn);
79 GNUNET_free (fn);
80 fn = NULL;
81 }
82}
83
84
85static void *
86progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event);
87
88
89static void
90restart_fs_task (void *cls)
91{
92 GNUNET_FS_stop (fs);
93 fs = GNUNET_FS_start (cfg, "test-fs-unindex-persistence", &progress_cb, NULL,
94 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
95}
96
97
98/**
99 * Consider scheduling the restart-task.
100 * Only runs the restart task once per event
101 * category.
102 *
103 * @param ev type of the event to consider
104 */
105static void
106consider_restart (int ev)
107{
108 static int prev[32];
109 static int off;
110 int i;
111
112 for (i = 0; i < off; i++)
113 if (prev[i] == ev)
114 return;
115 prev[off++] = ev;
116 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_URGENT,
117 &restart_fs_task, NULL);
118}
119
120
121static void *
122progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
123{
124 switch (event->status)
125 {
126 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
128 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
129 (unsigned long long) event->value.publish.completed,
130 (unsigned long long) event->value.publish.size,
131 event->value.publish.specifics.progress.depth,
132 (unsigned long long) event->value.publish.specifics.
133 progress.offset);
134 break;
135
136 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
137 break;
138
139 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
140 printf ("Publishing complete, %llu kbps.\n",
141 (unsigned long long) (FILESIZE * 1000000LL
142 / (1
143 + GNUNET_TIME_absolute_get_duration
144 (start).rel_value_us) / 1024));
145 start = GNUNET_TIME_absolute_get ();
146 unindex = GNUNET_FS_unindex_start (fs, fn, "unindex");
147 GNUNET_assert (unindex != NULL);
148 break;
149
150 case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
151 printf ("Unindex complete, %llu kbps.\n",
152 (unsigned long long) (FILESIZE * 1000000LL
153 / (1
154 + GNUNET_TIME_absolute_get_duration
155 (start).rel_value_us) / 1024));
156 GNUNET_SCHEDULER_add_now (&abort_unindex_task, NULL);
157 break;
158
159 case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
160 consider_restart (event->status);
161 GNUNET_assert (unindex == event->value.unindex.uc);
162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163 "Unindex is progressing (%llu/%llu at level %u off %llu)...\n",
164 (unsigned long long) event->value.unindex.completed,
165 (unsigned long long) event->value.unindex.size,
166 event->value.unindex.specifics.progress.depth,
167 (unsigned long long) event->value.unindex.specifics.
168 progress.offset);
169 break;
170
171 case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
172 if (event->value.publish.pc == publish)
173 publish = NULL;
174 break;
175
176 case GNUNET_FS_STATUS_PUBLISH_RESUME:
177 if (NULL == publish)
178 {
179 publish = event->value.publish.pc;
180 return "publish-context";
181 }
182 break;
183
184 case GNUNET_FS_STATUS_UNINDEX_SUSPEND:
185 GNUNET_assert (event->value.unindex.uc == unindex);
186 unindex = NULL;
187 break;
188
189 case GNUNET_FS_STATUS_UNINDEX_RESUME:
190 GNUNET_assert (NULL == unindex);
191 unindex = event->value.unindex.uc;
192 return "unindex";
193
194 case GNUNET_FS_STATUS_PUBLISH_ERROR:
195 fprintf (stderr, "Error publishing file: %s\n",
196 event->value.publish.specifics.error.message);
197 GNUNET_break (0);
198 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
199 break;
200
201 case GNUNET_FS_STATUS_UNINDEX_ERROR:
202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
203 "Error unindexing file: %s\n",
204 event->value.unindex.specifics.error.message);
205 GNUNET_SCHEDULER_add_now (&abort_unindex_task, NULL);
206 break;
207
208 case GNUNET_FS_STATUS_PUBLISH_START:
209 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
210 GNUNET_assert (NULL == event->value.publish.pctx);
211 GNUNET_assert (FILESIZE == event->value.publish.size);
212 GNUNET_assert (0 == event->value.publish.completed);
213 GNUNET_assert (1 == event->value.publish.anonymity);
214 break;
215
216 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
217 GNUNET_assert (publish == event->value.publish.pc);
218 GNUNET_assert (FILESIZE == event->value.publish.size);
219 GNUNET_assert (1 == event->value.publish.anonymity);
220 GNUNET_FS_stop (fs);
221 fs = NULL;
222 break;
223
224 case GNUNET_FS_STATUS_UNINDEX_START:
225 consider_restart (event->status);
226 GNUNET_assert (unindex == NULL);
227 GNUNET_assert (0 == strcmp ("unindex", event->value.unindex.cctx));
228 GNUNET_assert (0 == strcmp (fn, event->value.unindex.filename));
229 GNUNET_assert (FILESIZE == event->value.unindex.size);
230 GNUNET_assert (0 == event->value.unindex.completed);
231 break;
232
233 case GNUNET_FS_STATUS_UNINDEX_STOPPED:
234 GNUNET_assert (unindex == event->value.unindex.uc);
235 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
236 break;
237
238 default:
239 printf ("Unexpected event: %d\n", event->status);
240 break;
241 }
242 return NULL;
243}
244
245
246static void
247run (void *cls,
248 const struct GNUNET_CONFIGURATION_Handle *c,
249 struct GNUNET_TESTING_Peer *peer)
250{
251 const char *keywords[] = {
252 "down_foo",
253 "down_bar",
254 };
255 char *buf;
256 struct GNUNET_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
296int
297main (int argc, char *argv[])
298{
299 if (0 != GNUNET_TESTING_peer_run ("test-fs-unindex-persistence",
300 "test_fs_unindex_data.conf",
301 &run, NULL))
302 return 1;
303 return 0;
304}
305
306
307/* end of test_fs_unindex_persistence.c */
diff --git a/src/fs/test_fs_uri.c b/src/fs/test_fs_uri.c
deleted file mode 100644
index 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
31static int
32testKeyword ()
33{
34 char *uri;
35 struct GNUNET_FS_Uri *ret;
36 char *emsg;
37
38 if (NULL != (ret = GNUNET_FS_uri_parse ("gnunet://fs/ksk/++", &emsg)))
39 {
40 GNUNET_FS_uri_destroy (ret);
41 GNUNET_assert (0);
42 }
43 GNUNET_free (emsg);
44 ret = GNUNET_FS_uri_parse ("gnunet://fs/ksk/foo+bar", &emsg);
45 if (NULL == ret)
46 {
47 GNUNET_free (emsg);
48 GNUNET_assert (0);
49 }
50 if (! GNUNET_FS_uri_test_ksk (ret))
51 {
52 GNUNET_FS_uri_destroy (ret);
53 GNUNET_assert (0);
54 }
55 if ((2 != ret->data.ksk.keywordCount) ||
56 (0 != strcmp (" foo", ret->data.ksk.keywords[0])) ||
57 (0 != strcmp (" bar", ret->data.ksk.keywords[1])))
58 {
59 GNUNET_FS_uri_destroy (ret);
60 GNUNET_assert (0);
61 }
62
63 uri = GNUNET_FS_uri_to_string (ret);
64 if (0 != strcmp (uri, "gnunet://fs/ksk/foo+bar"))
65 {
66 GNUNET_free (uri);
67 GNUNET_FS_uri_destroy (ret);
68 GNUNET_assert (0);
69 }
70 GNUNET_free (uri);
71 GNUNET_FS_uri_destroy (ret);
72 return 0;
73}
74
75
76static int
77testLocation ()
78{
79 struct GNUNET_FS_Uri *uri;
80 char *uric;
81 struct GNUNET_FS_Uri *uri2;
82 struct GNUNET_FS_Uri *baseURI;
83 char *emsg = NULL;
84 struct GNUNET_CRYPTO_EddsaPrivateKey pk;
85
86 baseURI =
87 GNUNET_FS_uri_parse
88 (
89 "gnunet://fs/chk/4QZP479A9SKGFNMQ2ZBCYE71YV2QMTVGWTVPB6A10ASVCKXDHB05DKPSC7ZF6E9P9W1VE47394EQY7NXA47Q6R35M7P1MJPGP59D1Z8.D54QD1K5XCG5878T6YZ19AM60MQ6FC0YNVK7QY08KK0KM0FJJ3KQWYG112FN5T07KN7J0X35DF6WVBT9B8ZMZ3X2BXJ22X3KFQ6MV2G.15999",
90 &emsg);
91 GNUNET_assert (baseURI != NULL);
92 GNUNET_assert (emsg == NULL);
93 GNUNET_CRYPTO_eddsa_key_create (&pk);
94 uri = GNUNET_FS_uri_loc_create (baseURI,
95 &pk,
96 GNUNET_TIME_absolute_get ());
97 if (NULL == uri)
98 {
99 GNUNET_break (0);
100 GNUNET_FS_uri_destroy (baseURI);
101 return 1;
102 }
103 if (! GNUNET_FS_uri_test_loc (uri))
104 {
105 GNUNET_break (0);
106 GNUNET_FS_uri_destroy (uri);
107 GNUNET_FS_uri_destroy (baseURI);
108 return 1;
109 }
110 uri2 = GNUNET_FS_uri_loc_get_uri (uri);
111 if (! GNUNET_FS_uri_test_equal (baseURI, uri2))
112 {
113 GNUNET_break (0);
114 GNUNET_FS_uri_destroy (uri);
115 GNUNET_FS_uri_destroy (uri2);
116 GNUNET_FS_uri_destroy (baseURI);
117 return 1;
118 }
119 GNUNET_FS_uri_destroy (uri2);
120 GNUNET_FS_uri_destroy (baseURI);
121 uric = GNUNET_FS_uri_to_string (uri);
122#if 0
123 /* not for the faint of heart: */
124 printf ("URI: `%s'\n", uric);
125#endif
126 uri2 = GNUNET_FS_uri_parse (uric, &emsg);
127 GNUNET_free (uric);
128 if (uri2 == NULL)
129 {
130 fprintf (stderr, "URI parsing failed: %s\n", emsg);
131 GNUNET_break (0);
132 GNUNET_FS_uri_destroy (uri);
133 GNUNET_free (emsg);
134 return 1;
135 }
136 GNUNET_assert (NULL == emsg);
137 if (GNUNET_YES != GNUNET_FS_uri_test_equal (uri, uri2))
138 {
139 GNUNET_break (0);
140 GNUNET_FS_uri_destroy (uri);
141 GNUNET_FS_uri_destroy (uri2);
142 return 1;
143 }
144 GNUNET_FS_uri_destroy (uri2);
145 GNUNET_FS_uri_destroy (uri);
146 return 0;
147}
148
149
150static int
151testNamespace (int i)
152{
153 char *uri;
154 struct GNUNET_FS_Uri *ret;
155 char *emsg;
156 struct GNUNET_CRYPTO_EcdsaPrivateKey ph;
157 struct GNUNET_CRYPTO_EcdsaPublicKey id;
158 char buf[1024];
159 char ubuf[1024];
160 char *sret;
161
162 if (NULL !=
163 (ret =
164 GNUNET_FS_uri_parse (
165 "gnunet://fs/sks/D1KJS9H2A82Q65VKQ0ML3RFU6U1D3VUK",
166 &emsg)))
167 {
168 GNUNET_FS_uri_destroy (ret);
169 GNUNET_assert (0);
170 }
171 GNUNET_free (emsg);
172 if (NULL !=
173 (ret =
174 GNUNET_FS_uri_parse
175 (
176 "gnunet://fs/sks/XQHH4R288W26EBV369F6RCE0PJVJTX2Y74Q2FJPMPGA31HJX2JG/this",
177 &emsg)))
178 {
179 GNUNET_FS_uri_destroy (ret);
180 GNUNET_assert (0);
181 }
182 GNUNET_free (emsg);
183 if (NULL != (ret = GNUNET_FS_uri_parse ("gnunet://fs/sks/test", &emsg)))
184 {
185 GNUNET_FS_uri_destroy (ret);
186 GNUNET_assert (0);
187 }
188 GNUNET_free (emsg);
189 GNUNET_CRYPTO_ecdsa_key_create (&ph);
190 GNUNET_CRYPTO_ecdsa_key_get_public (&ph,
191 &id);
192 sret = GNUNET_STRINGS_data_to_string (&id, sizeof(id),
193 ubuf, sizeof(ubuf) - 1);
194 GNUNET_assert (NULL != sret);
195 sret[0] = '\0';
196 GNUNET_snprintf (buf, sizeof(buf),
197 "gnunet://fs/sks/%s/test",
198 ubuf);
199 ret = GNUNET_FS_uri_parse (buf, &emsg);
200 if (NULL == ret)
201 {
202 GNUNET_free (emsg);
203 GNUNET_assert (0);
204 }
205 if (GNUNET_FS_uri_test_ksk (ret))
206 {
207 GNUNET_FS_uri_destroy (ret);
208 GNUNET_assert (0);
209 }
210 if (! GNUNET_FS_uri_test_sks (ret))
211 {
212 GNUNET_FS_uri_destroy (ret);
213 GNUNET_assert (0);
214 }
215
216 uri = GNUNET_FS_uri_to_string (ret);
217 if (0 !=
218 strcmp (uri,
219 buf))
220 {
221 GNUNET_FS_uri_destroy (ret);
222 GNUNET_free (uri);
223 GNUNET_assert (0);
224 }
225 GNUNET_free (uri);
226 GNUNET_FS_uri_destroy (ret);
227 return 0;
228}
229
230
231static int
232testFile (int i)
233{
234 char *uri;
235 struct GNUNET_FS_Uri *ret;
236 char *emsg;
237
238 if (NULL !=
239 (ret =
240 GNUNET_FS_uri_parse
241 (
242 "gnunet://fs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H00000440000.42",
243 &emsg)))
244 {
245 GNUNET_FS_uri_destroy (ret);
246 GNUNET_assert (0);
247 }
248 GNUNET_free (emsg);
249 if (NULL !=
250 (ret =
251 GNUNET_FS_uri_parse
252 (
253 "gnunet://fs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000",
254 &emsg)))
255 {
256 GNUNET_FS_uri_destroy (ret);
257 GNUNET_assert (0);
258 }
259 GNUNET_free (emsg);
260 if (NULL !=
261 (ret =
262 GNUNET_FS_uri_parse
263 (
264 "gnunet://fs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.FGH",
265 &emsg)))
266 {
267 GNUNET_FS_uri_destroy (ret);
268 GNUNET_assert (0);
269 }
270 GNUNET_free (emsg);
271 ret =
272 GNUNET_FS_uri_parse
273 (
274 "gnunet://fs/chk/4QZP479A9SKGFNMQ2ZBCYE71YV2QMTVGWTVPB6A10ASVCKXDHB05DKPSC7ZF6E9P9W1VE47394EQY7NXA47Q6R35M7P1MJPGP59D1Z8.D54QD1K5XCG5878T6YZ19AM60MQ6FC0YNVK7QY08KK0KM0FJJ3KQWYG112FN5T07KN7J0X35DF6WVBT9B8ZMZ3X2BXJ22X3KFQ6MV2G.42",
275 &emsg);
276 if (ret == NULL)
277 {
278 GNUNET_free (emsg);
279 GNUNET_assert (0);
280 }
281 if (GNUNET_FS_uri_test_ksk (ret))
282 {
283 GNUNET_FS_uri_destroy (ret);
284 GNUNET_assert (0);
285 }
286 if (GNUNET_FS_uri_test_sks (ret))
287 {
288 GNUNET_FS_uri_destroy (ret);
289 GNUNET_assert (0);
290 }
291 if (GNUNET_ntohll (ret->data.chk.file_length) != 42)
292 {
293 GNUNET_FS_uri_destroy (ret);
294 GNUNET_assert (0);
295 }
296
297 uri = GNUNET_FS_uri_to_string (ret);
298 if (0 !=
299 strcmp (uri,
300 "gnunet://fs/chk/4QZP479A9SKGFNMQ2ZBCYE71YV2QMTVGWTVPB6A10ASVCKXDHB05DKPSC7ZF6E9P9W1VE47394EQY7NXA47Q6R35M7P1MJPGP59D1Z8.D54QD1K5XCG5878T6YZ19AM60MQ6FC0YNVK7QY08KK0KM0FJJ3KQWYG112FN5T07KN7J0X35DF6WVBT9B8ZMZ3X2BXJ22X3KFQ6MV2G.42"))
301 {
302 GNUNET_free (uri);
303 GNUNET_FS_uri_destroy (ret);
304 GNUNET_assert (0);
305 }
306 GNUNET_free (uri);
307 GNUNET_FS_uri_destroy (ret);
308 return 0;
309}
310
311
312int
313main (int argc, char *argv[])
314{
315 int failureCount = 0;
316 int i;
317
318 GNUNET_log_setup ("test_fs_uri",
319 "WARNING",
320 NULL);
321 failureCount += testKeyword ();
322 failureCount += testLocation ();
323 for (i = 0; i < 255; i++)
324 {
325 /* fprintf (stderr, "%s", "."); */
326 failureCount += testNamespace (i);
327 failureCount += testFile (i);
328 }
329 /* fprintf (stderr, "%s", "\n"); */
330 GNUNET_DISK_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)
21import sys
22import os
23import subprocess
24import re
25import shutil
26
27srcdir = "../.."
28gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts")
29if gnunet_pyexpect_dir not in sys.path:
30 sys.path.append(gnunet_pyexpect_dir)
31
32from gnunet_pyexpect import pexpect
33
34if os.name == 'posix':
35 download = './gnunet-download'
36 gnunetarm = 'gnunet-arm'
37 publish = './gnunet-publish'
38 unindex = './gnunet-unindex'
39elif os.name == 'nt':
40 download = './gnunet-download.exe'
41 gnunetarm = 'gnunet-arm.exe'
42 publish = './gnunet-publish.exe'
43 unindex = './gnunet-unindex.exe'
44
45if os.name == "nt":
46 shutil.rmtree(
47 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-idx"), True
48 )
49else:
50 shutil.rmtree("/tmp/gnunet-test-fs-py-idx", True)
51
52arm = subprocess.Popen([gnunetarm, '-sq', '-c', 'test_gnunet_fs_idx_data.conf'])
53arm.communicate()
54
55try:
56 pub = pexpect()
57
58 pub.spawn(
59 None, [
60 publish, '-c', 'test_gnunet_fs_idx_data.conf', '-m',
61 "description:Test archive", '-k', 'tst',
62 'test_gnunet_fs_rec_data.tgz'
63 ],
64 stdout=subprocess.PIPE,
65 stderr=subprocess.STDOUT
66 )
67 pub.expect(
68 "stdout",
69 re.compile(
70 r"URI is `gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR\.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG\.17822'\.\r?\n"
71 )
72 )
73
74 down = pexpect()
75 down.spawn(
76 None, [
77 download, '-c', 'test_gnunet_fs_idx_data.conf', '-o',
78 'test_gnunet_fs_rec_data.tar.gz',
79 'gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG.17822'
80 ],
81 stdout=subprocess.PIPE,
82 stderr=subprocess.STDOUT
83 )
84 down.expect(
85 "stdout",
86 re.compile(
87 r"Downloading `test_gnunet_fs_rec_data.tar.gz' done (.*).\r?\n"
88 )
89 )
90 os.remove("test_gnunet_fs_rec_data.tar.gz")
91
92 un = pexpect()
93 un.spawn(
94 None, [
95 unindex, '-c', 'test_gnunet_fs_idx_data.conf',
96 'test_gnunet_fs_rec_data.tgz'
97 ],
98 stdout=subprocess.PIPE,
99 stderr=subprocess.STDOUT
100 )
101 un.expect("stdout", re.compile(r'Unindexing done\.\r?\n'))
102
103finally:
104 arm = subprocess.Popen([
105 gnunetarm, '-eq', '-c', 'test_gnunet_fs_idx_data.conf'
106 ])
107 arm.communicate()
108 if os.name == "nt":
109 shutil.rmtree(
110 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-idx"), True
111 )
112 else:
113 shutil.rmtree("/tmp/gnunet-test-fs-py-idx", True)
diff --git a/src/fs/test_gnunet_fs_idx_data.conf b/src/fs/test_gnunet_fs_idx_data.conf
deleted file mode 100644
index ba2a872dd..000000000
--- a/src/fs/test_gnunet_fs_idx_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-py-idx/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_gnunet_fs_psd.py.in b/src/fs/test_gnunet_fs_psd.py.in
deleted file mode 100755
index a25e4eaef..000000000
--- a/src/fs/test_gnunet_fs_psd.py.in
+++ /dev/null
@@ -1,149 +0,0 @@
1#!@PYTHONEXE@
2# This file is part of GNUnet.
3# (C) 2010, 2018 Christian Grothoff (and other contributing authors)
4#
5# GNUnet is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# GNUnet is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19#
20# Testcase for file-sharing command-line tools (publish, search, download)
21
22import sys
23import os
24import subprocess
25import re
26import shutil
27try:
28 # Python 2.7
29 reload
30except NameError:
31 try:
32 # Python 3.4+:
33 from importlib import reload
34 except ImportError:
35 # Python 3.0 - 3.3
36 from imp import reload
37
38reload(sys)
39
40# Force encoding to utf-8, as this test otherwise fails
41# on some systems (see #5094). In Python 3+ there is no attribute
42# sys.setdefaultencoding anymore.
43if (3 < sys.version_info[0]):
44 sys.setdefaultencoding('utf8')
45
46srcdir = "../.."
47gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts")
48if gnunet_pyexpect_dir not in sys.path:
49 sys.path.append(gnunet_pyexpect_dir)
50
51from gnunet_pyexpect import pexpect
52
53if os.name == 'posix':
54 download = './gnunet-download'
55 gnunetarm = 'gnunet-arm'
56 publish = './gnunet-publish'
57 unindex = './gnunet-unindex'
58 search = './gnunet-search'
59elif os.name == 'nt':
60 download = './gnunet-download.exe'
61 gnunetarm = 'gnunet-arm.exe'
62 publish = './gnunet-publish.exe'
63 unindex = './gnunet-unindex.exe'
64 search = './gnunet-search.exe'
65
66if "GNUNET_PREFIX" in os.environ:
67 pass
68else:
69 print("You need to export GNUNET_PREFIX")
70 sys.exit(1)
71
72if os.name == "nt":
73 shutil.rmtree(
74 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-psd"), True
75 )
76else:
77 shutil.rmtree("/tmp/gnunet-test-fs-py-psd", True)
78
79arm = subprocess.Popen([gnunetarm, '-sq', '-c', 'test_gnunet_fs_psd_data.conf'])
80arm.communicate()
81
82# first, basic publish-search-download run
83try:
84 pub = pexpect()
85 pub.spawn(
86 None, [
87 publish, '-c', 'test_gnunet_fs_psd_data.conf', '-n', '-m',
88 "description:Test archive", '-k', 'tst',
89 'test_gnunet_fs_rec_data.tgz'
90 ],
91 stdout=subprocess.PIPE,
92 stderr=subprocess.STDOUT
93 )
94 pub.expect(
95 "stdout",
96 re.compile(r"Publishing `.+test_gnunet_fs_rec_data.tgz' done\.\r?\n")
97 )
98 pub.expect(
99 "stdout",
100 re.compile(
101 r"URI is `gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR\.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG\.17822'\.\r?\n"
102 )
103 )
104
105 s = pexpect()
106 s.spawn(
107 None, [
108 search, '-V', '-t', '1000 ms', '-N', '1', '-c',
109 'test_gnunet_fs_psd_data.conf', 'tst'
110 ],
111 stdout=subprocess.PIPE,
112 stderr=subprocess.STDOUT
113 )
114 s.expect(
115 "stdout",
116 re.compile(
117 r'gnunet-download -o "test_gnunet_fs_rec_data.tgz" gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR\.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG\.17822\r?\n'
118 )
119 )
120
121 down = pexpect()
122 down.spawn(
123 None, [
124 download, '-c', 'test_gnunet_fs_psd_data.conf', '-o',
125 'test_gnunet_fs_rec_data.tar.gz',
126 'gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG.17822'
127 ],
128 stdout=subprocess.PIPE,
129 stderr=subprocess.STDOUT
130 )
131 down.expect(
132 "stdout",
133 re.compile(
134 r"Downloading `test_gnunet_fs_rec_data.tar.gz' done (.*).\r?\n"
135 )
136 )
137 os.remove("test_gnunet_fs_rec_data.tar.gz")
138
139finally:
140 arm = subprocess.Popen([
141 gnunetarm, '-eq', '-c', 'test_gnunet_fs_psd_data.conf'
142 ])
143 arm.communicate()
144 if os.name == "nt":
145 shutil.rmtree(
146 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-psd"), True
147 )
148 else:
149 shutil.rmtree("/tmp/gnunet-test-fs-py-psd", True)
diff --git a/src/fs/test_gnunet_fs_psd_data.conf b/src/fs/test_gnunet_fs_psd_data.conf
deleted file mode 100644
index f6c05ecdd..000000000
--- a/src/fs/test_gnunet_fs_psd_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-py-psd/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_gnunet_fs_rec.py.in b/src/fs/test_gnunet_fs_rec.py.in
deleted file mode 100755
index f7e84e3da..000000000
--- a/src/fs/test_gnunet_fs_rec.py.in
+++ /dev/null
@@ -1,171 +0,0 @@
1#!@PYTHONEXE@
2# This file is part of GNUnet.
3# (C) 2010 Christian Grothoff (and other contributing authors)
4#
5# GNUnet is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# GNUnet is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19#
20# Testcase for file-sharing command-line tools (recursive publishing & download)
21import sys
22import os
23import subprocess
24import re
25import shutil
26import tarfile
27import filecmp
28
29srcdir = "../.."
30gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts")
31if gnunet_pyexpect_dir not in sys.path:
32 sys.path.append(gnunet_pyexpect_dir)
33
34from gnunet_pyexpect import pexpect
35from pydiffer import dcdiff
36
37if os.name == 'posix':
38 download = './gnunet-download'
39 gnunetarm = 'gnunet-arm'
40 publish = './gnunet-publish'
41 unindex = './gnunet-unindex'
42 search = './gnunet-search'
43 directory = './gnunet-directory'
44elif os.name == 'nt':
45 download = './gnunet-download.exe'
46 gnunetarm = 'gnunet-arm.exe'
47 publish = './gnunet-publish.exe'
48 unindex = './gnunet-unindex.exe'
49 search = './gnunet-search.exe'
50 directory = './gnunet-directory.exe'
51
52if os.name == "nt":
53 shutil.rmtree(
54 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-rec"), True
55 )
56else:
57 shutil.rmtree("/tmp/gnunet-test-fs-py-rec", True)
58
59arm = subprocess.Popen([gnunetarm, '-sq', '-c', 'test_gnunet_fs_rec_data.conf'])
60arm.communicate()
61
62tar = tarfile.open('test_gnunet_fs_rec_data.tgz')
63tar.extractall()
64# first, basic publish-search-download run
65try:
66 pub = pexpect()
67 pub.spawn(
68 None, [
69 publish, '-c', 'test_gnunet_fs_rec_data.conf', '-k', 'testdir',
70 'dir/'
71 ],
72 stdout=subprocess.PIPE,
73 stderr=subprocess.STDOUT
74 )
75 # Can't say much for publishing, except that the last one is the toplevel directory
76 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
77 pub.expect(
78 "stdout",
79 re.compile(
80 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
81 )
82 )
83 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
84 pub.expect(
85 "stdout",
86 re.compile(
87 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
88 )
89 )
90 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
91 pub.expect(
92 "stdout",
93 re.compile(
94 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
95 )
96 )
97 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
98 pub.expect(
99 "stdout",
100 re.compile(
101 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
102 )
103 )
104 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
105 pub.expect(
106 "stdout",
107 re.compile(
108 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
109 )
110 )
111 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
112 pub.expect(
113 "stdout",
114 re.compile(
115 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
116 )
117 )
118 pub.expect(
119 "stdout", re.compile(r"Publishing `.+[\\/]dir[\\/]' done\.\r?\n")
120 )
121 m = pub.expect("stdout", re.compile(r".+\r?\n"))
122 if not m:
123 sys.exit(3)
124 output = m.string
125 url = output[output.find("`") + 1:output.find("'")]
126
127 down = pexpect()
128 down.spawn(
129 None, [
130 download, '-c', 'test_gnunet_fs_rec_data.conf', '-R', '-o',
131 'rdir.gnd', url
132 ],
133 stdout=subprocess.PIPE,
134 stderr=subprocess.STDOUT
135 )
136 down.expect("stdout", re.compile(r"Downloading `rdir.gnd' done (.*).\r?\n"))
137
138 d = pexpect()
139 d.spawn(
140 None, [directory, '-c', 'test_gnunet_fs_rec_data.conf', 'rdir/a.gnd'],
141 stdout=subprocess.PIPE,
142 stderr=subprocess.STDOUT
143 )
144 d.expect("stdout", re.compile(r"Directory `a/' meta data:\r?\n"))
145 d.expect("stdout", re.compile(r"Directory `a/' contents:\r?\n"))
146 d.expect("stdout", re.compile(r"COPYING (.*)\r?\n"))
147 d.expect("stdout", re.compile(r"INSTALL (.*)\r?\n"))
148
149 os.remove("rdir/b.gnd")
150 os.remove("rdir/a.gnd")
151 diff = dcdiff('dir', 'rdir')
152 if len(diff) != 0:
153 raise Exception(
154 "Unexpected difference between source directory and downloaded result:\n{}"
155 .format(diff)
156 )
157
158finally:
159 arm = subprocess.Popen([
160 gnunetarm, '-eq', '-c', 'test_gnunet_fs_rec_data.conf'
161 ])
162 arm.communicate()
163 if os.name == "nt":
164 shutil.rmtree(
165 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-rec"), True
166 )
167 else:
168 shutil.rmtree("/tmp/gnunet-test-fs-py-rec", True)
169 shutil.rmtree("dir", True)
170 shutil.rmtree("rdir", True)
171 shutil.rmtree("rdir.gnd", True)
diff --git a/src/fs/test_gnunet_fs_rec_data.conf b/src/fs/test_gnunet_fs_rec_data.conf
deleted file mode 100644
index 82ddb8f49..000000000
--- a/src/fs/test_gnunet_fs_rec_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-py-rec/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_gnunet_fs_rec_data.tgz b/src/fs/test_gnunet_fs_rec_data.tgz
deleted file mode 100644
index 697794306..000000000
--- a/src/fs/test_gnunet_fs_rec_data.tgz
+++ /dev/null
Binary files differ
diff --git a/src/fs/test_gnunet_service_fs_migration.c b/src/fs/test_gnunet_service_fs_migration.c
deleted file mode 100644
index 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
50static struct GNUNET_TESTBED_Peer *daemons[2];
51
52static int ok;
53
54static struct GNUNET_TIME_Absolute start_time;
55
56static struct GNUNET_TESTBED_Operation *op;
57
58
59struct DownloadContext
60{
61 char *fn;
62
63 struct GNUNET_FS_Uri *uri;
64};
65
66
67static void
68do_stop (void *cls)
69{
70 struct GNUNET_TIME_Relative del;
71 char *fancy;
72
73 GNUNET_SCHEDULER_shutdown ();
74 if (0 ==
75 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
76 TIMEOUT)).
77 rel_value_us)
78 {
79 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
80 "Timeout during download, shutting down with error\n");
81 ok = 1;
82 }
83 else
84 {
85 del = GNUNET_TIME_absolute_get_duration (start_time);
86 if (del.rel_value_us == 0)
87 del.rel_value_us = 1;
88 fancy =
89 GNUNET_STRINGS_byte_size_fancy (((unsigned long long) FILESIZE)
90 * 1000000LL / del.rel_value_us);
91 fprintf (stdout,
92 "Download speed was %s/s\n",
93 fancy);
94 GNUNET_free (fancy);
95 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
96 "Finished download, shutting down\n");
97 }
98}
99
100
101static void
102do_download (void *cls,
103 const char *emsg)
104{
105 struct DownloadContext *dc = cls;
106 struct GNUNET_FS_Uri *uri = dc->uri;
107
108 GNUNET_TESTBED_operation_done (op);
109 op = NULL;
110 if (NULL != dc->fn)
111 {
112 GNUNET_DISK_directory_remove (dc->fn);
113 GNUNET_free (dc->fn);
114 }
115 GNUNET_free (dc);
116 if (NULL != emsg)
117 {
118 GNUNET_SCHEDULER_shutdown ();
119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
120 "Failed to stop source daemon: %s\n",
121 emsg);
122 GNUNET_FS_uri_destroy (uri);
123 ok = 1;
124 return;
125 }
126 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
127 "Downloading %llu bytes\n",
128 (unsigned long long) FILESIZE);
129 start_time = GNUNET_TIME_absolute_get ();
130 GNUNET_FS_TEST_download (daemons[0],
131 TIMEOUT,
132 1,
133 SEED,
134 uri,
135 VERBOSE,
136 &do_stop,
137 NULL);
138 GNUNET_FS_uri_destroy (uri);
139}
140
141
142static void
143stop_source_peer (void *cls)
144{
145 struct DownloadContext *dc = cls;
146
147 /* FIXME: We should not interact with testbed when shutting down */
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149 "Stopping source peer\n");
150 op = GNUNET_TESTBED_peer_stop (NULL,
151 daemons[1],
152 &do_download, dc);
153 GNUNET_assert (NULL != op);
154}
155
156
157static void
158do_wait (void *cls,
159 const struct GNUNET_FS_Uri *uri,
160 const char *fn)
161{
162 struct DownloadContext *dc;
163
164 if (NULL == uri)
165 {
166 GNUNET_SCHEDULER_shutdown ();
167 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
168 "Timeout during upload attempt, shutting down with error\n");
169 ok = 1;
170 return;
171 }
172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173 "Waiting to allow content to migrate\n");
174 dc = GNUNET_new (struct DownloadContext);
175 dc->uri = GNUNET_FS_uri_dup (uri);
176 if (NULL != fn)
177 dc->fn = GNUNET_strdup (fn);
178 (void) GNUNET_SCHEDULER_add_delayed (MIGRATION_DELAY,
179 &stop_source_peer,
180 dc);
181}
182
183
184static void
185do_publish (void *cls,
186 struct GNUNET_TESTBED_RunHandle *h,
187 unsigned int num_peers,
188 struct GNUNET_TESTBED_Peer **peers,
189 unsigned int links_succeeded,
190 unsigned int links_failed)
191{
192 unsigned int i;
193
194 GNUNET_assert (2 == num_peers);
195 for (i = 0; i < num_peers; i++)
196 daemons[i] = peers[i];
197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
198 "Publishing %llu bytes\n",
199 (unsigned long long) FILESIZE);
200 GNUNET_FS_TEST_publish (daemons[1], TIMEOUT, 1, GNUNET_NO,
201 FILESIZE, SEED,
202 VERBOSE, &do_wait, NULL);
203}
204
205
206int
207main (int argc,
208 char *argv[])
209{
210 (void) GNUNET_TESTBED_test_run ("test-gnunet-service-fs-migration",
211 "fs_test_lib_data.conf",
212 2,
213 0, NULL, NULL,
214 &do_publish,
215 NULL);
216 GNUNET_DISK_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]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-service-fs-migration/
4
5[testbed]
6OVERLAY_TOPOLOGY = CLIQUE
7
8[ats]
9WAN_QUOTA_IN = 3932160
10WAN_QUOTA_OUT = 3932160
diff --git a/src/fs/test_gnunet_service_fs_p2p.c b/src/fs/test_gnunet_service_fs_p2p.c
deleted file mode 100644
index 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
45static const char *progname;
46
47static unsigned int anonymity_level;
48
49static struct GNUNET_TESTBED_Peer *daemons[NUM_DAEMONS];
50
51static int ok;
52
53static struct GNUNET_TIME_Absolute start_time;
54
55
56static void
57do_stop (void *cls)
58{
59 char *fn = cls;
60 struct GNUNET_TIME_Relative del;
61 char *fancy;
62
63 GNUNET_SCHEDULER_shutdown ();
64 if (0 ==
65 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
66 TIMEOUT)).
67 rel_value_us)
68 {
69 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
70 "Timeout during download, shutting down with error\n");
71 ok = 1;
72 }
73 else
74 {
75 del = GNUNET_TIME_absolute_get_duration (start_time);
76 if (0 == del.rel_value_us)
77 del.rel_value_us = 1;
78 fancy =
79 GNUNET_STRINGS_byte_size_fancy (((unsigned long long) FILESIZE)
80 * 1000000LL / del.rel_value_us);
81 fprintf (stdout,
82 "Download speed was %s/s\n",
83 fancy);
84 GNUNET_free (fancy);
85 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86 "Finished download, shutting down\n");
87 }
88 if (NULL != fn)
89 {
90 GNUNET_DISK_directory_remove (fn);
91 GNUNET_free (fn);
92 }
93}
94
95
96static void
97do_download (void *cls, const struct GNUNET_FS_Uri *uri,
98 const char *fn)
99{
100 if (NULL == uri)
101 {
102 GNUNET_SCHEDULER_shutdown ();
103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
104 "Timeout during upload attempt, shutting down with error\n");
105 ok = 1;
106 return;
107 }
108 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Downloading %llu bytes\n",
109 (unsigned long long) FILESIZE);
110 start_time = GNUNET_TIME_absolute_get ();
111 GNUNET_FS_TEST_download (daemons[0], TIMEOUT,
112 anonymity_level, SEED, uri,
113 VERBOSE, &do_stop,
114 (NULL == fn)
115 ? NULL
116 : GNUNET_strdup (fn));
117}
118
119
120static void
121do_publish (void *cls,
122 struct GNUNET_TESTBED_RunHandle *h,
123 unsigned int num_peers,
124 struct GNUNET_TESTBED_Peer **peers,
125 unsigned int links_succeeded,
126 unsigned int links_failed)
127{
128 unsigned int i;
129
130 if (NULL != strstr (progname, "cadet"))
131 anonymity_level = 0;
132 else
133 anonymity_level = 1;
134 GNUNET_assert (NUM_DAEMONS == num_peers);
135 for (i = 0; i < num_peers; i++)
136 daemons[i] = peers[i];
137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
138 (unsigned long long) FILESIZE);
139 GNUNET_FS_TEST_publish (daemons[1], TIMEOUT,
140 anonymity_level, GNUNET_NO,
141 FILESIZE, SEED,
142 VERBOSE, &do_download, NULL);
143}
144
145
146int
147main (int argc, char *argv[])
148{
149 const char *config;
150
151 progname = argv[0];
152 if (NULL != strstr (progname, "cadet"))
153 config = "test_gnunet_service_fs_p2p_cadet.conf";
154 else
155 config = "fs_test_lib_data.conf";
156 (void) GNUNET_TESTBED_test_run ("test-gnunet-service-fs-p2p",
157 config,
158 NUM_DAEMONS,
159 0, NULL, NULL,
160 &do_publish, NULL);
161 GNUNET_DISK_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...
8DISABLE_ANON_TRANSFER = YES
9
10# Do we cache content from other nodes? (may improve anonymity)
11CONTENT_CACHING = NO
12
13# Do we send unsolicited data to other nodes if we have excess bandwidth?
14# (may improve anonymity, probably not a good idea if content_caching is NO)
15CONTENT_PUSHING = NO
16
17#PREFIX = valgrind
18
19[cadet]
20#PREFIX = valgrind
diff --git a/src/fs/test_plugin_block_fs.c b/src/fs/test_plugin_block_fs.c
deleted file mode 100644
index f15d10b17..000000000
--- a/src/fs/test_plugin_block_fs.c
+++ /dev/null
@@ -1,86 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_plugin_block_fs.c
22 * @brief test for plugin_block_fs.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_block_lib.h"
27
28
29static int
30test_fs (struct GNUNET_BLOCK_Context *ctx)
31{
32 struct GNUNET_HashCode key;
33 char block[4];
34
35 memset (block, 1, sizeof(block));
36 if (GNUNET_OK !=
37 GNUNET_BLOCK_get_key (ctx,
38 GNUNET_BLOCK_TYPE_FS_DBLOCK,
39 block,
40 sizeof(block),
41 &key))
42 return 1;
43 if (GNUNET_OK !=
44 GNUNET_BLOCK_check_block (ctx,
45 GNUNET_BLOCK_TYPE_FS_DBLOCK,
46 block,
47 sizeof(block)))
48 return 2;
49 if (GNUNET_OK !=
50 GNUNET_BLOCK_check_query (ctx,
51 GNUNET_BLOCK_TYPE_FS_DBLOCK,
52 &key,
53 NULL, 0))
54 return 4;
55 GNUNET_log_skip (1, GNUNET_NO);
56 if (GNUNET_NO !=
57 GNUNET_BLOCK_check_query (ctx,
58 GNUNET_BLOCK_TYPE_FS_DBLOCK,
59 &key,
60 "bogus", 5))
61 return 8;
62 GNUNET_log_skip (0, GNUNET_YES);
63 return 0;
64}
65
66
67int
68main (int argc, char *argv[])
69{
70 int ret;
71 struct GNUNET_BLOCK_Context *ctx;
72 struct GNUNET_CONFIGURATION_Handle *cfg;
73
74 GNUNET_log_setup ("test-block", "WARNING", NULL);
75 cfg = GNUNET_CONFIGURATION_create ();
76 ctx = GNUNET_BLOCK_context_create (cfg);
77 ret = test_fs (ctx);
78 GNUNET_BLOCK_context_destroy (ctx);
79 GNUNET_CONFIGURATION_destroy (cfg);
80 if (ret != 0)
81 fprintf (stderr, "Tests failed: %d\n", ret);
82 return ret;
83}
84
85
86/* end of test_plugin_block_fs.c */
diff --git a/src/fs/test_pseudonym_data.conf b/src/fs/test_pseudonym_data.conf
deleted file mode 100644
index 5827721b8..000000000
--- a/src/fs/test_pseudonym_data.conf
+++ /dev/null
@@ -1,6 +0,0 @@
1# General settings
2[fs]
3
4[TESTING]
5WEAKRANDOM = YES
6