aboutsummaryrefslogtreecommitdiff
path: root/src/service/fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/fs')
-rw-r--r--src/service/fs/.gitignore36
-rw-r--r--src/service/fs/Makefile.am376
-rw-r--r--src/service/fs/fs.conf.in63
-rw-r--r--src/service/fs/fs.h397
-rw-r--r--src/service/fs/fs_api.c3321
-rw-r--r--src/service/fs/fs_api.h1941
-rw-r--r--src/service/fs/fs_directory.c676
-rw-r--r--src/service/fs/fs_dirmetascan.c496
-rw-r--r--src/service/fs/fs_download.c2338
-rw-r--r--src/service/fs/fs_file_information.c407
-rw-r--r--src/service/fs/fs_getopt.c274
-rw-r--r--src/service/fs/fs_list_indexed.c219
-rw-r--r--src/service/fs/fs_misc.c165
-rw-r--r--src/service/fs/fs_namespace.c804
-rw-r--r--src/service/fs/fs_publish.c1625
-rw-r--r--src/service/fs/fs_publish_ksk.c255
-rw-r--r--src/service/fs/fs_publish_ublock.c299
-rw-r--r--src/service/fs/fs_publish_ublock.h109
-rw-r--r--src/service/fs/fs_search.c1826
-rw-r--r--src/service/fs/fs_sharetree.c457
-rw-r--r--src/service/fs/fs_tree.c452
-rw-r--r--src/service/fs/fs_tree.h217
-rw-r--r--src/service/fs/fs_unindex.c902
-rw-r--r--src/service/fs/fs_uri.c2062
-rw-r--r--src/service/fs/gnunet-daemon-fsprofiler.c673
-rw-r--r--src/service/fs/gnunet-fs-profiler.c245
-rw-r--r--src/service/fs/gnunet-helper-fs-publish.c579
-rw-r--r--src/service/fs/gnunet-service-fs.c1378
-rw-r--r--src/service/fs/gnunet-service-fs.h304
-rw-r--r--src/service/fs/gnunet-service-fs_cadet.h168
-rw-r--r--src/service/fs/gnunet-service-fs_cadet_client.c728
-rw-r--r--src/service/fs/gnunet-service-fs_cadet_server.c545
-rw-r--r--src/service/fs/gnunet-service-fs_cp.c1667
-rw-r--r--src/service/fs/gnunet-service-fs_cp.h415
-rw-r--r--src/service/fs/gnunet-service-fs_indexing.c495
-rw-r--r--src/service/fs/gnunet-service-fs_indexing.h120
-rw-r--r--src/service/fs/gnunet-service-fs_pe.c814
-rw-r--r--src/service/fs/gnunet-service-fs_pe.h95
-rw-r--r--src/service/fs/gnunet-service-fs_pr.c1887
-rw-r--r--src/service/fs/gnunet-service-fs_pr.h422
-rw-r--r--src/service/fs/gnunet-service-fs_push.c672
-rw-r--r--src/service/fs/gnunet-service-fs_push.h66
-rw-r--r--src/service/fs/gnunet-service-fs_put.c290
-rw-r--r--src/service/fs/gnunet-service-fs_put.h46
-rw-r--r--src/service/fs/meson.build82
-rw-r--r--src/service/fs/meta_data.c1238
-rw-r--r--src/service/fs/perf_gnunet_service_fs_p2p.c370
-rw-r--r--src/service/fs/perf_gnunet_service_fs_p2p.conf7
-rw-r--r--src/service/fs/perf_gnunet_service_fs_p2p_respect.c480
-rw-r--r--src/service/fs/test_fs.c262
-rw-r--r--src/service/fs/test_fs_data.conf7
-rw-r--r--src/service/fs/test_fs_defaults.conf47
-rw-r--r--src/service/fs/test_fs_directory.c186
-rw-r--r--src/service/fs/test_fs_download.c368
-rw-r--r--src/service/fs/test_fs_download_data.conf10
-rw-r--r--src/service/fs/test_fs_download_indexed.conf10
-rw-r--r--src/service/fs/test_fs_download_persistence.c351
-rw-r--r--src/service/fs/test_fs_file_information.c163
-rw-r--r--src/service/fs/test_fs_file_information_data.conf7
-rw-r--r--src/service/fs/test_fs_getopt.c37
-rw-r--r--src/service/fs/test_fs_list_indexed.c265
-rw-r--r--src/service/fs/test_fs_list_indexed_data.conf10
-rw-r--r--src/service/fs/test_fs_meta_data.c492
-rw-r--r--src/service/fs/test_fs_namespace.c320
-rw-r--r--src/service/fs/test_fs_namespace_data.conf7
-rw-r--r--src/service/fs/test_fs_namespace_list_updateable.c175
-rw-r--r--src/service/fs/test_fs_publish.c251
-rw-r--r--src/service/fs/test_fs_publish_data.conf10
-rw-r--r--src/service/fs/test_fs_publish_persistence.c322
-rw-r--r--src/service/fs/test_fs_search.c252
-rw-r--r--src/service/fs/test_fs_search_data.conf7
-rw-r--r--src/service/fs/test_fs_search_persistence.c318
-rw-r--r--src/service/fs/test_fs_search_probes.c258
-rw-r--r--src/service/fs/test_fs_search_with_and.c272
-rw-r--r--src/service/fs/test_fs_start_stop.c64
-rw-r--r--src/service/fs/test_fs_test_lib.c181
-rw-r--r--src/service/fs/test_fs_unindex.c237
-rw-r--r--src/service/fs/test_fs_unindex_data.conf7
-rw-r--r--src/service/fs/test_fs_unindex_persistence.c307
-rw-r--r--src/service/fs/test_fs_uri.c340
-rw-r--r--src/service/fs/test_gnunet_fs_rec_data.tgzbin0 -> 17822 bytes
-rw-r--r--src/service/fs/test_gnunet_service_fs_migration.c223
-rw-r--r--src/service/fs/test_gnunet_service_fs_migration_data.conf10
-rw-r--r--src/service/fs/test_gnunet_service_fs_p2p.c167
-rw-r--r--src/service/fs/test_gnunet_service_fs_p2p_cadet.conf20
-rw-r--r--src/service/fs/test_pseudonym_data.conf6
86 files changed, 39472 insertions, 0 deletions
diff --git a/src/service/fs/.gitignore b/src/service/fs/.gitignore
new file mode 100644
index 000000000..f3637f186
--- /dev/null
+++ b/src/service/fs/.gitignore
@@ -0,0 +1,36 @@
1gnunet-daemon-fsprofiler
2gnunet-fs-profiler
3gnunet-helper-fs-publish
4gnunet-service-fs
5test_fs_directory
6test_fs_download
7test_fs_download_cadet
8test_fs_download_indexed
9test_fs_download_persistence
10test_fs_file_information
11test_fs_getopt
12test_fs_list_indexed
13test_fs_namespace
14test_fs_namespace_list_updateable
15test_fs_publish
16test_fs_publish_persistence
17test_fs_search
18test_fs_search_persistence
19test_fs_search_probes
20test_fs_search_with_and
21test_fs_start_stop
22test_fs_test_lib
23test_fs_unindex
24test_fs_unindex_persistence
25test_fs_uri
26test_gnunet_fs_idx.py
27test_gnunet_fs_psd.py
28test_gnunet_fs_rec.py
29test_gnunet_service_fs_migration
30test_gnunet_service_fs_p2p
31test_gnunet_service_fs_p2p_cadet
32test_plugin_block_fs
33perf_gnunet_service_fs_p2p
34perf_gnunet_service_fs_p2p_index
35perf_gnunet_service_fs_p2p_respect
36rdir.gnd
diff --git a/src/service/fs/Makefile.am b/src/service/fs/Makefile.am
new file mode 100644
index 000000000..0a10f688a
--- /dev/null
+++ b/src/service/fs/Makefile.am
@@ -0,0 +1,376 @@
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
16lib_LTLIBRARIES = libgnunetfs.la
17
18libgnunetfs_la_SOURCES = \
19 fs_api.c fs_api.h fs.h \
20 fs_directory.c \
21 fs_dirmetascan.c \
22 fs_download.c \
23 fs_file_information.c \
24 fs_getopt.c \
25 fs_list_indexed.c \
26 fs_publish.c \
27 fs_publish_ksk.c \
28 fs_publish_ublock.c fs_publish_ublock.h \
29 fs_misc.c \
30 fs_namespace.c \
31 fs_search.c \
32 fs_sharetree.c \
33 fs_tree.c fs_tree.h \
34 fs_unindex.c \
35 fs_uri.c \
36 meta_data.c
37
38libgnunetfs_la_LIBADD = \
39 $(top_builddir)/src/service/datastore/libgnunetdatastore.la \
40 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
41 $(top_builddir)/src/lib/util/libgnunetutil.la \
42 $(GN_LIBINTL) $(XLIB) $(LIBGCRYPT_LIBS) -lunistring
43
44if HAVE_LIBEXTRACTOR
45libgnunetfs_la_LIBADD += \
46 -lextractor
47endif
48
49libgnunetfs_la_LDFLAGS = \
50 $(GN_LIB_LDFLAGS) \
51 -version-info 3:1:1
52
53
54libexec_PROGRAMS = \
55 gnunet-helper-fs-publish \
56 gnunet-service-fs
57
58noinst_PROGRAMS = \
59 gnunet-daemon-fsprofiler
60
61gnunet_helper_fs_publish_SOURCES = \
62 gnunet-helper-fs-publish.c
63gnunet_helper_fs_publish_LDADD = \
64 libgnunetfs.la \
65 $(top_builddir)/src/lib/util/libgnunetutil.la \
66 $(GN_LIBINTL)
67
68if HAVE_LIBEXTRACTOR
69gnunet_helper_fs_publish_LDADD += \
70 -lextractor
71endif
72
73gnunet_daemon_fsprofiler_SOURCES = \
74 gnunet-daemon-fsprofiler.c
75gnunet_daemon_fsprofiler_LDADD = \
76 libgnunetfs.la \
77 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
78 $(top_builddir)/src/lib/util/libgnunetutil.la \
79 $(GN_LIBINTL)
80
81gnunet_service_fs_SOURCES = \
82 gnunet-service-fs.c gnunet-service-fs.h \
83 gnunet-service-fs_cp.c gnunet-service-fs_cp.h \
84 gnunet-service-fs_indexing.c gnunet-service-fs_indexing.h \
85 gnunet-service-fs_pe.c gnunet-service-fs_pe.h \
86 gnunet-service-fs_pr.c gnunet-service-fs_pr.h \
87 gnunet-service-fs_push.c gnunet-service-fs_push.h \
88 gnunet-service-fs_put.c gnunet-service-fs_put.h \
89 gnunet-service-fs_cadet_client.c gnunet-service-fs_cadet.h \
90 gnunet-service-fs_cadet_server.c
91gnunet_service_fs_LDADD = \
92 libgnunetfs.la \
93 $(top_builddir)/src/service/dht/libgnunetdht.la \
94 $(top_builddir)/src/lib/block/libgnunetblock.la \
95 $(top_builddir)/src/service/datastore/libgnunetdatastore.la \
96 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
97 $(top_builddir)/src/service/cadet/libgnunetcadet.la \
98 $(top_builddir)/src/service/core/libgnunetcore.la \
99 $(top_builddir)/src/lib/util/libgnunetutil.la \
100 $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \
101 $(GN_LIBINTL) -lm
102
103check_PROGRAMS = \
104 test_fs_directory \
105 test_fs_download \
106 test_fs_download_cadet \
107 test_fs_download_indexed \
108 test_fs_download_persistence \
109 test_fs_file_information \
110 test_fs_getopt \
111 test_fs_list_indexed \
112 test_fs_namespace \
113 test_fs_namespace_list_updateable \
114 test_fs_publish \
115 test_fs_publish_persistence \
116 test_fs_search \
117 test_fs_search_with_and \
118 test_fs_search_probes \
119 test_fs_search_persistence \
120 test_fs_start_stop \
121 test_fs_unindex \
122 test_fs_unindex_persistence \
123 test_fs_uri \
124 test_fs_meta_data \
125 $(FS_BENCHMARKS)
126# test_gnunet_service_fs_migration
127
128if ENABLE_TEST_RUN
129AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
130TESTS = test_fs_directory \
131 test_fs_file_information \
132 test_fs_namespace \
133 test_fs_namespace_list_updateable \
134 test_fs_search \
135 test_fs_search_with_and \
136 test_fs_search_probes \
137 test_fs_search_persistence \
138 test_fs_start_stop \
139 test_fs_uri \
140 test_fs_meta_data
141endif
142
143
144test_fs_directory_SOURCES = \
145 test_fs_directory.c
146test_fs_directory_LDADD = \
147 libgnunetfs.la \
148 $(top_builddir)/src/lib/util/libgnunetutil.la
149
150if HAVE_LIBEXTRACTOR
151test_fs_directory_LDADD += \
152 -lextractor
153endif
154
155
156test_fs_download_SOURCES = \
157 test_fs_download.c
158test_fs_download_LDADD = \
159 $(top_builddir)/src/service/testing/libgnunettesting.la \
160 libgnunetfs.la \
161 $(top_builddir)/src/lib/util/libgnunetutil.la
162
163test_fs_download_indexed_SOURCES = \
164 test_fs_download.c
165test_fs_download_indexed_LDADD = \
166 $(top_builddir)/src/service/testing/libgnunettesting.la \
167 libgnunetfs.la \
168 $(top_builddir)/src/lib/util/libgnunetutil.la
169
170test_fs_download_cadet_SOURCES = \
171 test_fs_download.c
172test_fs_download_cadet_LDADD = \
173 $(top_builddir)/src/service/testing/libgnunettesting.la \
174 libgnunetfs.la \
175 $(top_builddir)/src/lib/util/libgnunetutil.la
176
177test_fs_download_persistence_SOURCES = \
178 test_fs_download_persistence.c
179test_fs_download_persistence_LDADD = \
180 $(top_builddir)/src/service/testing/libgnunettesting.la \
181 libgnunetfs.la \
182 $(top_builddir)/src/lib/util/libgnunetutil.la
183
184test_fs_file_information_SOURCES = \
185 test_fs_file_information.c
186test_fs_file_information_LDADD = \
187 libgnunetfs.la \
188 $(top_builddir)/src/lib/util/libgnunetutil.la
189
190if HAVE_LIBEXTRACTOR
191test_fs_file_information_LDADD += \
192 -lextractor
193endif
194
195
196test_fs_getopt_SOURCES = \
197 test_fs_getopt.c
198test_fs_getopt_LDADD = \
199 libgnunetfs.la \
200 $(top_builddir)/src/lib/util/libgnunetutil.la
201
202test_fs_list_indexed_SOURCES = \
203 test_fs_list_indexed.c
204test_fs_list_indexed_LDADD = \
205 $(top_builddir)/src/service/testing/libgnunettesting.la \
206 libgnunetfs.la \
207 $(top_builddir)/src/lib/util/libgnunetutil.la
208
209test_fs_namespace_SOURCES = \
210 test_fs_namespace.c
211test_fs_namespace_LDADD = \
212 $(top_builddir)/src/service/testing/libgnunettesting.la \
213 libgnunetfs.la \
214 $(top_builddir)/src/lib/util/libgnunetutil.la
215
216test_fs_namespace_list_updateable_SOURCES = \
217 test_fs_namespace_list_updateable.c
218test_fs_namespace_list_updateable_LDADD = \
219 $(top_builddir)/src/service/testing/libgnunettesting.la \
220 libgnunetfs.la \
221 $(top_builddir)/src/lib/util/libgnunetutil.la
222
223test_fs_publish_SOURCES = \
224 test_fs_publish.c
225test_fs_publish_LDADD = \
226 $(top_builddir)/src/service/testing/libgnunettesting.la \
227 libgnunetfs.la \
228 $(top_builddir)/src/lib/util/libgnunetutil.la
229
230test_fs_publish_persistence_SOURCES = \
231 test_fs_publish_persistence.c
232test_fs_publish_persistence_LDADD = \
233 $(top_builddir)/src/service/testing/libgnunettesting.la \
234 libgnunetfs.la \
235 $(top_builddir)/src/lib/util/libgnunetutil.la
236
237test_fs_search_SOURCES = \
238 test_fs_search.c
239test_fs_search_LDADD = \
240 $(top_builddir)/src/service/testing/libgnunettesting.la \
241 libgnunetfs.la \
242 $(top_builddir)/src/lib/util/libgnunetutil.la
243
244test_fs_search_with_and_SOURCES = \
245 test_fs_search_with_and.c
246test_fs_search_with_and_LDADD = \
247 $(top_builddir)/src/service/testing/libgnunettesting.la \
248 libgnunetfs.la \
249 $(top_builddir)/src/lib/util/libgnunetutil.la
250
251test_fs_search_probes_SOURCES = \
252 test_fs_search_probes.c
253test_fs_search_probes_LDADD = \
254 $(top_builddir)/src/service/testing/libgnunettesting.la \
255 libgnunetfs.la \
256 $(top_builddir)/src/lib/util/libgnunetutil.la
257
258test_fs_search_persistence_SOURCES = \
259 test_fs_search_persistence.c
260test_fs_search_persistence_LDADD = \
261 $(top_builddir)/src/service/testing/libgnunettesting.la \
262 libgnunetfs.la \
263 $(top_builddir)/src/lib/util/libgnunetutil.la
264
265test_fs_start_stop_SOURCES = \
266 test_fs_start_stop.c
267test_fs_start_stop_LDADD = \
268 $(top_builddir)/src/service/testing/libgnunettesting.la \
269 libgnunetfs.la \
270 $(top_builddir)/src/lib/util/libgnunetutil.la
271
272test_fs_unindex_SOURCES = \
273 test_fs_unindex.c
274test_fs_unindex_LDADD = \
275 $(top_builddir)/src/service/testing/libgnunettesting.la \
276 libgnunetfs.la \
277 $(top_builddir)/src/lib/util/libgnunetutil.la
278
279test_fs_unindex_persistence_SOURCES = \
280 test_fs_unindex_persistence.c
281test_fs_unindex_persistence_LDADD = \
282 $(top_builddir)/src/service/testing/libgnunettesting.la \
283 libgnunetfs.la \
284 $(top_builddir)/src/lib/util/libgnunetutil.la
285
286test_fs_meta_data_SOURCES = \
287 test_fs_meta_data.c
288test_fs_meta_data_LDADD = \
289 libgnunetfs.la \
290 $(top_builddir)/src/lib/util/libgnunetutil.la
291
292
293test_fs_uri_SOURCES = \
294 test_fs_uri.c
295test_fs_uri_LDADD = \
296 libgnunetfs.la \
297 $(top_builddir)/src/lib/util/libgnunetutil.la
298
299# TNG
300
301#test_gnunet_service_fs_p2p_SOURCES = \
302# test_gnunet_service_fs_p2p.c
303#test_gnunet_service_fs_p2p_LDADD = \
304# libgnunetfstest.a \
305# $(top_builddir)/src/testbed/libgnunettestbed.la \
306# libgnunetfs.la \
307# $(top_builddir)/src/lib/util/libgnunetutil.la
308#
309#test_gnunet_service_fs_p2p_cadet_SOURCES = \
310# test_gnunet_service_fs_p2p.c
311#test_gnunet_service_fs_p2p_cadet_LDADD = \
312# libgnunetfstest.a \
313# $(top_builddir)/src/testbed/libgnunettestbed.la \
314# libgnunetfs.la \
315# $(top_builddir)/src/lib/util/libgnunetutil.la
316#
317#test_gnunet_service_fs_migration_SOURCES = \
318# test_gnunet_service_fs_migration.c
319#test_gnunet_service_fs_migration_LDADD = \
320# libgnunetfstest.a \
321# $(top_builddir)/src/testbed/libgnunettestbed.la \
322# libgnunetfs.la \
323# $(top_builddir)/src/lib/util/libgnunetutil.la
324#
325#perf_gnunet_service_fs_p2p_SOURCES = \
326# perf_gnunet_service_fs_p2p.c
327#perf_gnunet_service_fs_p2p_LDADD = \
328# libgnunetfstest.a \
329# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
330# $(top_builddir)/src/testbed/libgnunettestbed.la \
331# libgnunetfs.la \
332# $(top_builddir)/src/lib/util/libgnunetutil.la
333#
334#perf_gnunet_service_fs_p2p_index_SOURCES = \
335# perf_gnunet_service_fs_p2p.c
336#perf_gnunet_service_fs_p2p_index_LDADD = \
337# libgnunetfstest.a \
338# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
339# $(top_builddir)/src/testbed/libgnunettestbed.la \
340# libgnunetfs.la \
341# $(top_builddir)/src/lib/util/libgnunetutil.la
342#
343#perf_gnunet_service_fs_p2p_dht_SOURCES = \
344# perf_gnunet_service_fs_p2p.c
345#perf_gnunet_service_fs_p2p_dht_LDADD = \
346# libgnunetfstest.a \
347# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
348# $(top_builddir)/src/testbed/libgnunettestbed.la \
349# libgnunetfs.la \
350# $(top_builddir)/src/lib/util/libgnunetutil.la
351#
352#perf_gnunet_service_fs_p2p_respect_SOURCES = \
353# perf_gnunet_service_fs_p2p_respect.c
354#perf_gnunet_service_fs_p2p_respect_LDADD = \
355# libgnunetfstest.a \
356# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
357# $(top_builddir)/src/testbed/libgnunettestbed.la \
358# libgnunetfs.la \
359# $(top_builddir)/src/lib/util/libgnunetutil.la
360
361EXTRA_DIST = \
362 perf_gnunet_service_fs_p2p.conf \
363 test_fs_data.conf \
364 test_fs_defaults.conf \
365 test_fs_download_data.conf \
366 test_fs_download_indexed.conf \
367 test_fs_file_information_data.conf \
368 test_fs_list_indexed_data.conf \
369 test_fs_namespace_data.conf \
370 test_fs_publish_data.conf \
371 test_fs_search_data.conf \
372 test_fs_unindex_data.conf \
373 test_gnunet_fs_rec_data.tgz \
374 test_gnunet_service_fs_migration_data.conf \
375 test_gnunet_service_fs_p2p_cadet.conf \
376 test_pseudonym_data.conf
diff --git a/src/service/fs/fs.conf.in b/src/service/fs/fs.conf.in
new file mode 100644
index 000000000..be02619bf
--- /dev/null
+++ b/src/service/fs/fs.conf.in
@@ -0,0 +1,63 @@
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/service/fs/fs.h b/src/service/fs/fs.h
new file mode 100644
index 000000000..c3bae65d2
--- /dev/null
+++ b/src/service/fs/fs.h
@@ -0,0 +1,397 @@
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/service/fs/fs_api.c b/src/service/fs/fs_api.c
new file mode 100644
index 000000000..65646c7eb
--- /dev/null
+++ b/src/service/fs/fs_api.c
@@ -0,0 +1,3321 @@
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 = ((enum GNUNET_FS_OPTIONS) va_arg (ap, int))))
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/service/fs/fs_api.h b/src/service/fs/fs_api.h
new file mode 100644
index 000000000..fdda91928
--- /dev/null
+++ b/src/service/fs/fs_api.h
@@ -0,0 +1,1941 @@
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/service/fs/fs_directory.c b/src/service/fs/fs_directory.c
new file mode 100644
index 000000000..c693f9216
--- /dev/null
+++ b/src/service/fs/fs_directory.c
@@ -0,0 +1,676 @@
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/service/fs/fs_dirmetascan.c b/src/service/fs/fs_dirmetascan.c
new file mode 100644
index 000000000..2379e29ce
--- /dev/null
+++ b/src/service/fs/fs_dirmetascan.c
@@ -0,0 +1,496 @@
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/service/fs/fs_download.c b/src/service/fs/fs_download.c
new file mode 100644
index 000000000..cee64466d
--- /dev/null
+++ b/src/service/fs/fs_download.c
@@ -0,0 +1,2338 @@
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 "Internal error or bogus download URI (expected %llu bytes at depth %u and offset %llu/%llu, got %llu bytes)",
1045 (unsigned long long) bs,
1046 dr->depth,
1047 (unsigned long long) dr->offset,
1048 (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length),
1049 (unsigned long long) prc->size);
1050 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s\n", dc->emsg);
1051 while (NULL != dr->parent)
1052 {
1053 dr->state = BRS_ERROR;
1054 dr = dr->parent;
1055 }
1056 dr->state = BRS_ERROR;
1057 goto signal_error;
1058 }
1059
1060 (void) GNUNET_CONTAINER_multihashmap_remove (dc->active, &prc->query, dr);
1061 GNUNET_CRYPTO_hash_to_aes_key (&dr->chk.key, &skey, &iv);
1062 if (-1 ==
1063 GNUNET_CRYPTO_symmetric_decrypt (prc->data, prc->size, &skey, &iv, pt))
1064 {
1065 GNUNET_break (0);
1066 dc->emsg = GNUNET_strdup (_ ("internal error decrypting content"));
1067 goto signal_error;
1068 }
1069 off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length),
1070 dr->offset,
1071 dr->depth);
1072 /* save to disk */
1073 if ((GNUNET_YES == prc->do_store) &&
1074 ((NULL != dc->filename) || (is_recursive_download (dc))) &&
1075 ((dr->depth == dc->treedepth) ||
1076 (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES))))
1077 {
1078 fh = GNUNET_DISK_file_open (NULL != dc->filename ? dc->filename
1079 : dc->temp_filename,
1080 GNUNET_DISK_OPEN_READWRITE
1081 | GNUNET_DISK_OPEN_CREATE,
1082 GNUNET_DISK_PERM_USER_READ
1083 | GNUNET_DISK_PERM_USER_WRITE
1084 | GNUNET_DISK_PERM_GROUP_READ
1085 | GNUNET_DISK_PERM_OTHER_READ);
1086 if (NULL == fh)
1087 {
1088 GNUNET_asprintf (&dc->emsg,
1089 _ ("Download failed: could not open file `%s': %s"),
1090 dc->filename,
1091 strerror (errno));
1092 goto signal_error;
1093 }
1094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1095 "Saving decrypted block to disk at offset %llu\n",
1096 (unsigned long long) off);
1097 if ((off != GNUNET_DISK_file_seek (fh, off, GNUNET_DISK_SEEK_SET)))
1098 {
1099 GNUNET_asprintf (&dc->emsg,
1100 _ ("Failed to seek to offset %llu in file `%s': %s"),
1101 (unsigned long long) off,
1102 dc->filename,
1103 strerror (errno));
1104 goto signal_error;
1105 }
1106 if (prc->size != GNUNET_DISK_file_write (fh, pt, prc->size))
1107 {
1108 GNUNET_asprintf (
1109 &dc->emsg,
1110 _ ("Failed to write block of %u bytes at offset %llu in file `%s': %s"),
1111 (unsigned int) prc->size,
1112 (unsigned long long) off,
1113 dc->filename,
1114 strerror (errno));
1115 goto signal_error;
1116 }
1117 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
1118 fh = NULL;
1119 }
1120
1121 if (0 == dr->depth)
1122 {
1123 /* DBLOCK, update progress and try recursion if applicable */
1124 app = prc->size;
1125 if (dr->offset < dc->offset)
1126 {
1127 /* starting offset begins in the middle of pt,
1128 * do not count first bytes as progress */
1129 GNUNET_assert (app > (dc->offset - dr->offset));
1130 app -= (dc->offset - dr->offset);
1131 }
1132 if (dr->offset + prc->size > dc->offset + dc->length)
1133 {
1134 /* end of block is after relevant range,
1135 * do not count last bytes as progress */
1136 GNUNET_assert (app >
1137 (dr->offset + prc->size) - (dc->offset + dc->length));
1138 app -= (dr->offset + prc->size) - (dc->offset + dc->length);
1139 }
1140 dc->completed += app;
1141
1142 /* do recursive download if option is set and either meta data
1143 * says it is a directory or if no meta data is given AND filename
1144 * ends in '.gnd' (top-level case) */
1145 if (is_recursive_download (dc))
1146 GNUNET_FS_directory_list_contents (prc->size,
1147 pt,
1148 off,
1149 &trigger_recursive_download,
1150 dc);
1151 }
1152 GNUNET_assert (dc->completed <= dc->length);
1153 dr->state = BRS_DOWNLOAD_DOWN;
1154 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1155 pi.value.download.specifics.progress.data = pt;
1156 pi.value.download.specifics.progress.offset = dr->offset;
1157 pi.value.download.specifics.progress.data_len = prc->size;
1158 pi.value.download.specifics.progress.depth = dr->depth;
1159 pi.value.download.specifics.progress.respect_offered = prc->respect_offered;
1160 pi.value.download.specifics.progress.num_transmissions =
1161 prc->num_transmissions;
1162 if (prc->last_transmission.abs_value_us !=
1163 GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
1164 pi.value.download.specifics.progress.block_download_duration =
1165 GNUNET_TIME_absolute_get_duration (prc->last_transmission);
1166 else
1167 pi.value.download.specifics.progress.block_download_duration =
1168 GNUNET_TIME_UNIT_ZERO; /* found locally */
1169 GNUNET_FS_download_make_status_ (&pi, dc);
1170 if (0 == dr->depth)
1171 propagate_up (dr);
1172
1173 if (dc->completed == dc->length)
1174 {
1175 /* download completed, signal */
1176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1177 "Download completed, truncating file to desired length %llu\n",
1178 (unsigned long long) GNUNET_ntohll (
1179 dc->uri->data.chk.file_length));
1180 /* truncate file to size (since we store IBlocks at the end) */
1181 if (NULL != dc->filename)
1182 {
1183 if (0 != truncate (dc->filename,
1184 GNUNET_ntohll (dc->uri->data.chk.file_length)))
1185 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1186 "truncate",
1187 dc->filename);
1188 }
1189 GNUNET_assert (0 == dr->depth);
1190 check_completed (dc);
1191 }
1192 if (0 == dr->depth)
1193 {
1194 /* bottom of the tree, no child downloads possible, just sync */
1195 GNUNET_FS_download_sync_ (dc);
1196 return GNUNET_YES;
1197 }
1198
1199 GNUNET_log (
1200 GNUNET_ERROR_TYPE_DEBUG,
1201 "Triggering downloads of children (this block was at depth %u and offset %llu)\n",
1202 dr->depth,
1203 (unsigned long long) dr->offset);
1204 GNUNET_assert (0 == (prc->size % sizeof(struct ContentHashKey)));
1205 chkarr = (struct ContentHashKey *) pt;
1206 for (i = dr->num_children - 1; i >= 0; i--)
1207 {
1208 drc = dr->children[i];
1209 switch (drc->state)
1210 {
1211 case BRS_INIT:
1212 if ((drc->chk_idx + 1) * sizeof(struct ContentHashKey) > prc->size)
1213 {
1214 /* 'chkarr' does not have enough space for this chk_idx;
1215 internal error! */
1216 GNUNET_break (0);
1217 GNUNET_assert (0);
1218 dc->emsg = GNUNET_strdup (_ ("internal error decoding tree"));
1219 goto signal_error;
1220 }
1221 drc->chk = chkarr[drc->chk_idx];
1222 drc->state = BRS_CHK_SET;
1223 if (GNUNET_YES == dc->issue_requests)
1224 schedule_block_download (dc, drc);
1225 break;
1226
1227 case BRS_RECONSTRUCT_DOWN:
1228 GNUNET_assert (0);
1229 break;
1230
1231 case BRS_RECONSTRUCT_META_UP:
1232 GNUNET_assert (0);
1233 break;
1234
1235 case BRS_RECONSTRUCT_UP:
1236 GNUNET_assert (0);
1237 break;
1238
1239 case BRS_CHK_SET:
1240 GNUNET_assert (0);
1241 break;
1242
1243 case BRS_DOWNLOAD_DOWN:
1244 GNUNET_assert (0);
1245 break;
1246
1247 case BRS_DOWNLOAD_UP:
1248 GNUNET_assert (0);
1249 break;
1250
1251 case BRS_ERROR:
1252 GNUNET_assert (0);
1253 break;
1254
1255 default:
1256 GNUNET_assert (0);
1257 break;
1258 }
1259 }
1260 GNUNET_FS_download_sync_ (dc);
1261 return GNUNET_YES;
1262
1263signal_error:
1264 if (NULL != fh)
1265 GNUNET_DISK_file_close (fh);
1266 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1267 pi.value.download.specifics.error.message = dc->emsg;
1268 GNUNET_FS_download_make_status_ (&pi, dc);
1269 GNUNET_MQ_destroy (dc->mq);
1270 dc->mq = NULL;
1271 GNUNET_FS_free_download_request_ (dc->top_request);
1272 dc->top_request = NULL;
1273 if (NULL != dc->job_queue)
1274 {
1275 GNUNET_FS_dequeue_ (dc->job_queue);
1276 dc->job_queue = NULL;
1277 }
1278 GNUNET_FS_download_sync_ (dc);
1279 return GNUNET_NO;
1280}
1281
1282
1283/**
1284 * Type of a function to call when we check the PUT message
1285 * from the service.
1286 *
1287 * @param cls closure
1288 * @param msg message received
1289 */
1290static int
1291check_put (void *cls, const struct ClientPutMessage *cm)
1292{
1293 /* any varsize length is OK */
1294 return GNUNET_OK;
1295}
1296
1297
1298/**
1299 * Type of a function to call when we receive a message
1300 * from the service.
1301 *
1302 * @param cls closure
1303 * @param msg message received
1304 */
1305static void
1306handle_put (void *cls, const struct ClientPutMessage *cm)
1307{
1308 struct GNUNET_FS_DownloadContext *dc = cls;
1309 uint16_t msize = ntohs (cm->header.size) - sizeof(*cm);
1310 struct ProcessResultClosure prc;
1311
1312 prc.dc = dc;
1313 prc.data = &cm[1];
1314 prc.last_transmission = GNUNET_TIME_absolute_ntoh (cm->last_transmission);
1315 prc.size = msize;
1316 prc.type = ntohl (cm->type);
1317 prc.do_store = GNUNET_YES;
1318 prc.respect_offered = ntohl (cm->respect_offered);
1319 prc.num_transmissions = ntohl (cm->num_transmissions);
1320 GNUNET_CRYPTO_hash (prc.data, msize, &prc.query);
1321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1322 "Received result for query `%s' from FS service\n",
1323 GNUNET_h2s (&prc.query));
1324 GNUNET_CONTAINER_multihashmap_get_multiple (dc->active,
1325 &prc.query,
1326 &process_result_with_request,
1327 &prc);
1328}
1329
1330
1331/**
1332 * Generic error handler, called with the appropriate error code and
1333 * the same closure specified at the creation of the message queue.
1334 * Not every message queue implementation supports an error handler.
1335 *
1336 * @param cls closure with the `struct GNUNET_FS_DownloadContext *`
1337 * @param error error code
1338 */
1339static void
1340download_mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
1341{
1342 struct GNUNET_FS_DownloadContext *dc = cls;
1343
1344 if (NULL != dc->mq)
1345 {
1346 GNUNET_MQ_destroy (dc->mq);
1347 dc->mq = NULL;
1348 }
1349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1350 "Transmitting download request failed, trying to reconnect\n");
1351 try_reconnect (dc);
1352}
1353
1354
1355/**
1356 * Reconnect to the FS service and transmit our queries NOW.
1357 *
1358 * @param cls our download context
1359 */
1360static void
1361do_reconnect (void *cls)
1362{
1363 struct GNUNET_FS_DownloadContext *dc = cls;
1364 struct GNUNET_MQ_MessageHandler handlers[] =
1365 { GNUNET_MQ_hd_var_size (put,
1366 GNUNET_MESSAGE_TYPE_FS_PUT,
1367 struct ClientPutMessage,
1368 dc),
1369 GNUNET_MQ_handler_end () };
1370
1371 dc->task = NULL;
1372 dc->mq = GNUNET_CLIENT_connect (dc->h->cfg,
1373 "fs",
1374 handlers,
1375 &download_mq_error_handler,
1376 dc);
1377 if (NULL == dc->mq)
1378 {
1379 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1380 "Connecting to `%s'-service failed, will try again.\n",
1381 "FS");
1382 try_reconnect (dc);
1383 return;
1384 }
1385 GNUNET_CONTAINER_multihashmap_iterate (dc->active, &retry_entry, dc);
1386}
1387
1388
1389/**
1390 * We've lost our connection with the FS service.
1391 * Re-establish it and re-transmit all of our
1392 * pending requests.
1393 *
1394 * @param dc download context that is having trouble
1395 */
1396static void
1397try_reconnect (struct GNUNET_FS_DownloadContext *dc)
1398{
1399 if (NULL != dc->mq)
1400 {
1401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1402 "Moving all requests back to pending list\n");
1403 GNUNET_MQ_destroy (dc->mq);
1404 dc->mq = NULL;
1405 }
1406 if (0 == dc->reconnect_backoff.rel_value_us)
1407 dc->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
1408 else
1409 dc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (dc->reconnect_backoff);
1410
1411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1412 "Will try to reconnect in %s\n",
1413 GNUNET_STRINGS_relative_time_to_string (dc->reconnect_backoff,
1414 GNUNET_YES));
1415 GNUNET_break (NULL != dc->job_queue);
1416 dc->task =
1417 GNUNET_SCHEDULER_add_delayed (dc->reconnect_backoff, &do_reconnect, dc);
1418}
1419
1420
1421/**
1422 * We're allowed to ask the FS service for our blocks. Start the download.
1423 *
1424 * @param cls the 'struct GNUNET_FS_DownloadContext'
1425 */
1426static void
1427activate_fs_download (void *cls)
1428{
1429 struct GNUNET_FS_DownloadContext *dc = cls;
1430 struct GNUNET_FS_ProgressInfo pi;
1431
1432 GNUNET_assert (NULL == dc->mq);
1433 GNUNET_assert (NULL != dc->active);
1434 do_reconnect (dc);
1435 if (NULL != dc->mq)
1436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download activated\n");
1437 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
1438 GNUNET_FS_download_make_status_ (&pi, dc);
1439}
1440
1441
1442/**
1443 * We must stop to ask the FS service for our blocks. Pause the download.
1444 *
1445 * @param cls the `struct GNUNET_FS_DownloadContext`
1446 */
1447static void
1448deactivate_fs_download (void *cls)
1449{
1450 struct GNUNET_FS_DownloadContext *dc = cls;
1451 struct GNUNET_FS_ProgressInfo pi;
1452
1453 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download deactivated\n");
1454 if (NULL != dc->mq)
1455 {
1456 GNUNET_MQ_destroy (dc->mq);
1457 dc->mq = NULL;
1458 }
1459 pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
1460 GNUNET_FS_download_make_status_ (&pi, dc);
1461}
1462
1463
1464/**
1465 * (recursively) Create a download request structure.
1466 *
1467 * @param parent parent of the current entry
1468 * @param chk_idx index of the chk for this block in the parent block
1469 * @param depth depth of the current entry, 0 are the DBLOCKs,
1470 * top level block is 'dc->treedepth - 1'
1471 * @param dr_offset offset in the original file this block maps to
1472 * (as in, offset of the first byte of the first DBLOCK
1473 * in the subtree rooted in the returned download request tree)
1474 * @param file_start_offset desired starting offset for the download
1475 * in the original file; requesting tree should not contain
1476 * DBLOCKs prior to the file_start_offset
1477 * @param desired_length desired number of bytes the user wanted to access
1478 * (from file_start_offset). Resulting tree should not contain
1479 * DBLOCKs after file_start_offset + file_length.
1480 * @return download request tree for the given range of DBLOCKs at
1481 * the specified depth
1482 */
1483static struct DownloadRequest *
1484create_download_request (struct DownloadRequest *parent,
1485 unsigned int chk_idx,
1486 unsigned int depth,
1487 uint64_t dr_offset,
1488 uint64_t file_start_offset,
1489 uint64_t desired_length)
1490{
1491 struct DownloadRequest *dr;
1492 unsigned int i;
1493 unsigned int head_skip;
1494 uint64_t child_block_size;
1495
1496 dr = GNUNET_new (struct DownloadRequest);
1497 dr->parent = parent;
1498 dr->depth = depth;
1499 dr->offset = dr_offset;
1500 dr->chk_idx = chk_idx;
1501 if (0 == depth)
1502 return dr;
1503 child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
1504
1505 /* calculate how many blocks at this level are not interesting
1506 * from the start (rounded down), either because of the requested
1507 * file offset or because this IBlock is further along */
1508 if (dr_offset < file_start_offset)
1509 {
1510 head_skip = (file_start_offset - dr_offset) / child_block_size;
1511 }
1512 else
1513 {
1514 head_skip = 0;
1515 }
1516
1517 /* calculate index of last block at this level that is interesting (rounded up) */
1518 dr->num_children =
1519 (file_start_offset + desired_length - dr_offset) / child_block_size;
1520 if (dr->num_children * child_block_size <
1521 file_start_offset + desired_length - dr_offset)
1522 dr->num_children++; /* round up */
1523 GNUNET_assert (dr->num_children > head_skip);
1524 dr->num_children -= head_skip;
1525 if (dr->num_children > CHK_PER_INODE)
1526 dr->num_children = CHK_PER_INODE; /* cap at max */
1527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1528 "Block at offset %llu and depth %u has %u children\n",
1529 (unsigned long long) dr_offset,
1530 depth,
1531 dr->num_children);
1532
1533 /* now we can get the total number of *interesting* children for this block */
1534
1535 /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
1536 GNUNET_assert (dr->num_children > 0);
1537
1538 dr->children = GNUNET_new_array (dr->num_children, struct DownloadRequest *);
1539 for (i = 0; i < dr->num_children; i++)
1540 {
1541 dr->children[i] =
1542 create_download_request (dr,
1543 i + head_skip,
1544 depth - 1,
1545 dr_offset + (i + head_skip) * child_block_size,
1546 file_start_offset,
1547 desired_length);
1548 }
1549 return dr;
1550}
1551
1552
1553/**
1554 * Continuation after a possible attempt to reconstruct
1555 * the current IBlock from the existing file.
1556 *
1557 * @param cls the 'struct ReconstructContext'
1558 */
1559static void
1560reconstruct_cont (void *cls)
1561{
1562 struct GNUNET_FS_DownloadContext *dc = cls;
1563
1564 /* clean up state from tree encoder */
1565 if (NULL != dc->task)
1566 {
1567 GNUNET_SCHEDULER_cancel (dc->task);
1568 dc->task = NULL;
1569 }
1570 if (NULL != dc->rfh)
1571 {
1572 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
1573 dc->rfh = NULL;
1574 }
1575 /* start "normal" download */
1576 dc->issue_requests = GNUNET_YES;
1577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting normal download\n");
1578 schedule_block_download (dc, dc->top_request);
1579}
1580
1581
1582/**
1583 * Task requesting the next block from the tree encoder.
1584 *
1585 * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing
1586 */
1587static void
1588get_next_block (void *cls)
1589{
1590 struct GNUNET_FS_DownloadContext *dc = cls;
1591
1592 dc->task = NULL;
1593 GNUNET_FS_tree_encoder_next (dc->te);
1594}
1595
1596
1597/**
1598 * Function called asking for the current (encoded)
1599 * block to be processed. After processing the
1600 * client should either call "GNUNET_FS_tree_encode_next"
1601 * or (on error) "GNUNET_FS_tree_encode_finish".
1602 *
1603 * This function checks if the content on disk matches
1604 * the expected content based on the URI.
1605 *
1606 * @param cls closure
1607 * @param chk content hash key for the block
1608 * @param offset offset of the block
1609 * @param depth depth of the block, 0 for DBLOCK
1610 * @param type type of the block (IBLOCK or DBLOCK)
1611 * @param block the (encrypted) block
1612 * @param block_size size of block (in bytes)
1613 */
1614static void
1615reconstruct_cb (void *cls,
1616 const struct ContentHashKey *chk,
1617 uint64_t offset,
1618 unsigned int depth,
1619 enum GNUNET_BLOCK_Type type,
1620 const void *block,
1621 uint16_t block_size)
1622{
1623 struct GNUNET_FS_DownloadContext *dc = cls;
1624 struct GNUNET_FS_ProgressInfo pi;
1625 struct DownloadRequest *dr;
1626 uint64_t blen;
1627 unsigned int chld;
1628
1629 /* find corresponding request entry */
1630 dr = dc->top_request;
1631 while (dr->depth > depth)
1632 {
1633 GNUNET_assert (dr->num_children > 0);
1634 blen = GNUNET_FS_tree_compute_tree_size (dr->depth - 1);
1635 chld = (offset - dr->offset) / blen;
1636 if (chld < dr->children[0]->chk_idx)
1637 {
1638 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1639 "Block %u < %u irrelevant for our range\n",
1640 chld,
1641 dr->children[0]->chk_idx);
1642 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1643 return; /* irrelevant block */
1644 }
1645 if (chld > dr->children[dr->num_children - 1]->chk_idx)
1646 {
1647 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1648 "Block %u > %u irrelevant for our range\n",
1649 chld,
1650 dr->children[dr->num_children - 1]->chk_idx);
1651 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1652 return; /* irrelevant block */
1653 }
1654 dr = dr->children[chld - dr->children[0]->chk_idx];
1655 }
1656 GNUNET_log (
1657 GNUNET_ERROR_TYPE_DEBUG,
1658 "Matched TE block with request at offset %llu and depth %u in state %d\n",
1659 (unsigned long long) dr->offset,
1660 dr->depth,
1661 dr->state);
1662 /* FIXME: this code needs more testing and might
1663 need to handle more states... */
1664 switch (dr->state)
1665 {
1666 case BRS_INIT:
1667 break;
1668
1669 case BRS_RECONSTRUCT_DOWN:
1670 break;
1671
1672 case BRS_RECONSTRUCT_META_UP:
1673 break;
1674
1675 case BRS_RECONSTRUCT_UP:
1676 break;
1677
1678 case BRS_CHK_SET:
1679 if (0 == memcmp (chk, &dr->chk, sizeof(struct ContentHashKey)))
1680 {
1681 GNUNET_log (
1682 GNUNET_ERROR_TYPE_DEBUG,
1683 "Reconstruction succeeded, can use block at offset %llu, depth %u\n",
1684 (unsigned long long) offset,
1685 depth);
1686 /* block matches, hence tree below matches;
1687 * this request is done! */
1688 dr->state = BRS_DOWNLOAD_UP;
1689 (void) GNUNET_CONTAINER_multihashmap_remove (dc->active,
1690 &dr->chk.query,
1691 dr);
1692 /* calculate how many bytes of payload this block
1693 * corresponds to */
1694 blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1695 /* how many of those bytes are in the requested range? */
1696 blen = GNUNET_MIN (blen, dc->length + dc->offset - dr->offset);
1697 /* signal progress */
1698 dc->completed += blen;
1699 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1700 pi.value.download.specifics.progress.data = NULL;
1701 pi.value.download.specifics.progress.offset = offset;
1702 pi.value.download.specifics.progress.data_len = 0;
1703 pi.value.download.specifics.progress.depth = 0;
1704 pi.value.download.specifics.progress.respect_offered = 0;
1705 pi.value.download.specifics.progress.block_download_duration =
1706 GNUNET_TIME_UNIT_ZERO;
1707 GNUNET_FS_download_make_status_ (&pi, dc);
1708 /* FIXME: duplicated code from 'process_result_with_request - refactor */
1709 if (dc->completed == dc->length)
1710 {
1711 /* download completed, signal */
1712 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1713 "Download completed, truncating file to desired length %llu\n",
1714 (unsigned long long) GNUNET_ntohll (
1715 dc->uri->data.chk.file_length));
1716 /* truncate file to size (since we store IBlocks at the end) */
1717 if (NULL != dc->filename)
1718 {
1719 if (0 != truncate (dc->filename,
1720 GNUNET_ntohll (dc->uri->data.chk.file_length)))
1721 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1722 "truncate",
1723 dc->filename);
1724 }
1725 }
1726 }
1727 else
1728 GNUNET_log (
1729 GNUNET_ERROR_TYPE_DEBUG,
1730 "Reconstruction failed, need to download block at offset %llu, depth %u\n",
1731 (unsigned long long) offset,
1732 depth);
1733 break;
1734
1735 case BRS_DOWNLOAD_DOWN:
1736 break;
1737
1738 case BRS_DOWNLOAD_UP:
1739 break;
1740
1741 case BRS_ERROR:
1742 break;
1743
1744 default:
1745 GNUNET_assert (0);
1746 break;
1747 }
1748 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1749 if ((dr == dc->top_request) && (dr->state == BRS_DOWNLOAD_UP))
1750 check_completed (dc);
1751}
1752
1753
1754/**
1755 * Function called by the tree encoder to obtain a block of plaintext
1756 * data (for the lowest level of the tree).
1757 *
1758 * @param cls our 'struct ReconstructContext'
1759 * @param offset identifies which block to get
1760 * @param max (maximum) number of bytes to get; returning
1761 * fewer will also cause errors
1762 * @param buf where to copy the plaintext buffer
1763 * @param emsg location to store an error message (on error)
1764 * @return number of bytes copied to buf, 0 on error
1765 */
1766static size_t
1767fh_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg)
1768{
1769 struct GNUNET_FS_DownloadContext *dc = cls;
1770 struct GNUNET_DISK_FileHandle *fh = dc->rfh;
1771 ssize_t ret;
1772
1773 if (NULL != emsg)
1774 *emsg = NULL;
1775 if (offset != GNUNET_DISK_file_seek (fh, offset, GNUNET_DISK_SEEK_SET))
1776 {
1777 if (NULL != emsg)
1778 *emsg = GNUNET_strdup (strerror (errno));
1779 return 0;
1780 }
1781 ret = GNUNET_DISK_file_read (fh, buf, max);
1782 if (ret < 0)
1783 {
1784 if (NULL != emsg)
1785 *emsg = GNUNET_strdup (strerror (errno));
1786 return 0;
1787 }
1788 return ret;
1789}
1790
1791
1792/**
1793 * Task that creates the initial (top-level) download
1794 * request for the file.
1795 *
1796 * @param cls the 'struct GNUNET_FS_DownloadContext'
1797 */
1798void
1799GNUNET_FS_download_start_task_ (void *cls)
1800{
1801 struct GNUNET_FS_DownloadContext *dc = cls;
1802 struct GNUNET_FS_ProgressInfo pi;
1803 struct GNUNET_DISK_FileHandle *fh;
1804
1805 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start task running...\n");
1806 dc->task = NULL;
1807 if (0 == dc->length)
1808 {
1809 /* no bytes required! */
1810 if (NULL != dc->filename)
1811 {
1812 fh = GNUNET_DISK_file_open (dc->filename,
1813 GNUNET_DISK_OPEN_READWRITE
1814 | GNUNET_DISK_OPEN_CREATE
1815 | ((0 ==
1816 GNUNET_FS_uri_chk_get_file_size (dc->uri))
1817 ? GNUNET_DISK_OPEN_TRUNCATE
1818 : 0),
1819 GNUNET_DISK_PERM_USER_READ
1820 | GNUNET_DISK_PERM_USER_WRITE
1821 | GNUNET_DISK_PERM_GROUP_READ
1822 | GNUNET_DISK_PERM_OTHER_READ);
1823 GNUNET_DISK_file_close (fh);
1824 }
1825 GNUNET_FS_download_sync_ (dc);
1826 pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
1827 pi.value.download.specifics.start.meta = dc->meta;
1828 GNUNET_FS_download_make_status_ (&pi, dc);
1829 check_completed (dc);
1830 return;
1831 }
1832 if (NULL != dc->emsg)
1833 return;
1834 if (NULL == dc->top_request)
1835 {
1836 dc->top_request = create_download_request (NULL,
1837 0,
1838 dc->treedepth - 1,
1839 0,
1840 dc->offset,
1841 dc->length);
1842 dc->top_request->state = BRS_CHK_SET;
1843 dc->top_request->chk = (dc->uri->type == GNUNET_FS_URI_CHK)
1844 ? dc->uri->data.chk.chk
1845 : dc->uri->data.loc.fi.chk;
1846 /* signal start */
1847 GNUNET_FS_download_sync_ (dc);
1848 if (NULL != dc->search)
1849 GNUNET_FS_search_result_sync_ (dc->search);
1850 pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
1851 pi.value.download.specifics.start.meta = dc->meta;
1852 GNUNET_FS_download_make_status_ (&pi, dc);
1853 }
1854 GNUNET_FS_download_start_downloading_ (dc);
1855 /* attempt reconstruction from disk */
1856 if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename))
1857 dc->rfh = GNUNET_DISK_file_open (dc->filename,
1858 GNUNET_DISK_OPEN_READ,
1859 GNUNET_DISK_PERM_NONE);
1860 if (dc->top_request->state == BRS_CHK_SET)
1861 {
1862 if (NULL != dc->rfh)
1863 {
1864 /* first, try top-down */
1865 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1866 "Trying top-down reconstruction for `%s'\n",
1867 dc->filename);
1868 try_top_down_reconstruction (dc, dc->top_request);
1869 switch (dc->top_request->state)
1870 {
1871 case BRS_CHK_SET:
1872 break; /* normal */
1873
1874 case BRS_DOWNLOAD_DOWN:
1875 break; /* normal, some blocks already down */
1876
1877 case BRS_DOWNLOAD_UP:
1878 /* already done entirely, party! */
1879 if (NULL != dc->rfh)
1880 {
1881 /* avoid hanging on to file handle longer than
1882 * necessary */
1883 GNUNET_DISK_file_close (dc->rfh);
1884 dc->rfh = NULL;
1885 }
1886 return;
1887
1888 case BRS_ERROR:
1889 GNUNET_asprintf (&dc->emsg, _ ("Invalid URI"));
1890 GNUNET_FS_download_sync_ (dc);
1891 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1892 pi.value.download.specifics.error.message = dc->emsg;
1893 GNUNET_FS_download_make_status_ (&pi, dc);
1894 return;
1895
1896 default:
1897 GNUNET_assert (0);
1898 break;
1899 }
1900 }
1901 }
1902 /* attempt reconstruction from meta data */
1903 if ((GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
1904 (NULL != dc->meta))
1905 {
1906 GNUNET_log (
1907 GNUNET_ERROR_TYPE_DEBUG,
1908 "Trying to find embedded meta data for download of size %llu with %u bytes MD\n",
1909 (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri),
1910 (unsigned int) GNUNET_FS_meta_data_get_serialized_size (dc->meta));
1911 GNUNET_FS_meta_data_iterate (dc->meta, &match_full_data, dc);
1912 if (BRS_DOWNLOAD_UP == dc->top_request->state)
1913 {
1914 if (NULL != dc->rfh)
1915 {
1916 /* avoid hanging on to file handle longer than
1917 * necessary */
1918 GNUNET_DISK_file_close (dc->rfh);
1919 dc->rfh = NULL;
1920 }
1921 return; /* finished, status update was already done for us */
1922 }
1923 }
1924 if (NULL != dc->rfh)
1925 {
1926 /* finally, actually run bottom-up */
1927 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1928 "Trying bottom-up reconstruction of file `%s'\n",
1929 dc->filename);
1930 dc->te =
1931 GNUNET_FS_tree_encoder_create (dc->h,
1932 GNUNET_FS_uri_chk_get_file_size (dc->uri),
1933 dc,
1934 &fh_reader,
1935 &reconstruct_cb,
1936 NULL,
1937 &reconstruct_cont);
1938 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1939 }
1940 else
1941 {
1942 /* simple, top-level download */
1943 dc->issue_requests = GNUNET_YES;
1944 schedule_block_download (dc, dc->top_request);
1945 }
1946 if (BRS_DOWNLOAD_UP == dc->top_request->state)
1947 check_completed (dc);
1948}
1949
1950
1951void
1952GNUNET_FS_download_signal_suspend_ (void *cls)
1953{
1954 struct GNUNET_FS_DownloadContext *dc = cls;
1955 struct GNUNET_FS_ProgressInfo pi;
1956
1957 if (NULL != dc->top)
1958 GNUNET_FS_end_top (dc->h, dc->top);
1959 while (NULL != dc->child_head)
1960 GNUNET_FS_download_signal_suspend_ (dc->child_head);
1961 if (NULL != dc->search)
1962 {
1963 dc->search->download = NULL;
1964 dc->search = NULL;
1965 }
1966 if (NULL != dc->job_queue)
1967 {
1968 GNUNET_FS_dequeue_ (dc->job_queue);
1969 dc->job_queue = NULL;
1970 }
1971 if (NULL != dc->parent)
1972 GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
1973 dc->parent->child_tail,
1974 dc);
1975 if (NULL != dc->task)
1976 {
1977 GNUNET_SCHEDULER_cancel (dc->task);
1978 dc->task = NULL;
1979 }
1980 pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
1981 GNUNET_FS_download_make_status_ (&pi, dc);
1982 if (NULL != dc->te)
1983 {
1984 GNUNET_FS_tree_encoder_finish (dc->te, NULL);
1985 dc->te = NULL;
1986 }
1987 if (NULL != dc->rfh)
1988 {
1989 GNUNET_DISK_file_close (dc->rfh);
1990 dc->rfh = NULL;
1991 }
1992 GNUNET_FS_free_download_request_ (dc->top_request);
1993 if (NULL != dc->active)
1994 {
1995 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
1996 dc->active = NULL;
1997 }
1998 GNUNET_free (dc->filename);
1999 GNUNET_FS_meta_data_destroy (dc->meta);
2000 GNUNET_FS_uri_destroy (dc->uri);
2001 GNUNET_free (dc->temp_filename);
2002 GNUNET_free (dc->serialization);
2003 GNUNET_assert (NULL == dc->job_queue);
2004 GNUNET_free (dc);
2005}
2006
2007
2008/**
2009 * Helper function to setup the download context.
2010 *
2011 * @param h handle to the file sharing subsystem
2012 * @param uri the URI of the file (determines what to download); CHK or LOC URI
2013 * @param meta known metadata for the file (can be NULL)
2014 * @param filename where to store the file, maybe NULL (then no file is
2015 * created on disk and data must be grabbed from the callbacks)
2016 * @param tempname where to store temporary file data, not used if filename is non-NULL;
2017 * can be NULL (in which case we will pick a name if needed); the temporary file
2018 * may already exist, in which case we will try to use the data that is there and
2019 * if it is not what is desired, will overwrite it
2020 * @param offset at what offset should we start the download (typically 0)
2021 * @param length how many bytes should be downloaded starting at offset
2022 * @param anonymity anonymity level to use for the download
2023 * @param options various options
2024 * @param cctx initial value for the client context for this download
2025 * @return context that can be used to control this download
2026 */
2027struct GNUNET_FS_DownloadContext *
2028create_download_context (struct GNUNET_FS_Handle *h,
2029 const struct GNUNET_FS_Uri *uri,
2030 const struct GNUNET_FS_MetaData *meta,
2031 const char *filename,
2032 const char *tempname,
2033 uint64_t offset,
2034 uint64_t length,
2035 uint32_t anonymity,
2036 enum GNUNET_FS_DownloadOptions options,
2037 void *cctx)
2038{
2039 struct GNUNET_FS_DownloadContext *dc;
2040
2041 GNUNET_assert (GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri));
2042 if ((offset + length < offset) ||
2043 (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)))
2044 {
2045 GNUNET_break (0);
2046 return NULL;
2047 }
2048 dc = GNUNET_new (struct GNUNET_FS_DownloadContext);
2049 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2050 "Starting download %p, %u bytes at offset %llu\n",
2051 dc,
2052 (unsigned int) length,
2053 (unsigned long long) offset);
2054 dc->h = h;
2055 dc->uri = GNUNET_FS_uri_dup (uri);
2056 dc->meta = GNUNET_FS_meta_data_duplicate (meta);
2057 dc->client_info = cctx;
2058 dc->start_time = GNUNET_TIME_absolute_get ();
2059 if (NULL != filename)
2060 {
2061 dc->filename = GNUNET_strdup (filename);
2062 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2063 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_size (filename,
2064 &dc->old_file_size,
2065 GNUNET_YES,
2066 GNUNET_YES));
2067 }
2068 if (GNUNET_FS_uri_test_loc (dc->uri))
2069 GNUNET_assert (GNUNET_OK ==
2070 GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target));
2071 dc->offset = offset;
2072 dc->length = length;
2073 dc->anonymity = anonymity;
2074 dc->options = options;
2075 dc->active =
2076 GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE),
2077 GNUNET_NO);
2078 dc->treedepth =
2079 GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri));
2080 if ((NULL == filename) && (is_recursive_download (dc)))
2081 {
2082 if (NULL != tempname)
2083 dc->temp_filename = GNUNET_strdup (tempname);
2084 else
2085 dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
2086 }
2087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2088 "Starting download `%s' of %llu bytes with tree depth %u\n",
2089 filename,
2090 (unsigned long long) length,
2091 dc->treedepth);
2092 GNUNET_assert (NULL == dc->job_queue);
2093 dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
2094 return dc;
2095}
2096
2097
2098struct GNUNET_FS_DownloadContext *
2099GNUNET_FS_download_start (struct GNUNET_FS_Handle *h,
2100 const struct GNUNET_FS_Uri *uri,
2101 const struct GNUNET_FS_MetaData *meta,
2102 const char *filename,
2103 const char *tempname,
2104 uint64_t offset,
2105 uint64_t length,
2106 uint32_t anonymity,
2107 enum GNUNET_FS_DownloadOptions options,
2108 void *cctx,
2109 struct GNUNET_FS_DownloadContext *parent)
2110{
2111 struct GNUNET_FS_DownloadContext *dc;
2112
2113 dc = create_download_context (h,
2114 uri,
2115 meta,
2116 filename,
2117 tempname,
2118 offset,
2119 length,
2120 anonymity,
2121 options,
2122 cctx);
2123 if (NULL == dc)
2124 return NULL;
2125 dc->parent = parent;
2126 if (NULL != parent)
2127 GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc);
2128 else if (0 == (GNUNET_FS_DOWNLOAD_IS_PROBE & options))
2129 dc->top =
2130 GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc);
2131 return dc;
2132}
2133
2134
2135struct GNUNET_FS_DownloadContext *
2136GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
2137 struct GNUNET_FS_SearchResult *sr,
2138 const char *filename,
2139 const char *tempname,
2140 uint64_t offset,
2141 uint64_t length,
2142 uint32_t anonymity,
2143 enum GNUNET_FS_DownloadOptions options,
2144 void *cctx)
2145{
2146 struct GNUNET_FS_DownloadContext *dc;
2147
2148 if ((NULL == sr) || (NULL != sr->download))
2149 {
2150 GNUNET_break (0);
2151 return NULL;
2152 }
2153 dc = create_download_context (h,
2154 sr->uri,
2155 sr->meta,
2156 filename,
2157 tempname,
2158 offset,
2159 length,
2160 anonymity,
2161 options,
2162 cctx);
2163 if (NULL == dc)
2164 return NULL;
2165 dc->search = sr;
2166 sr->download = dc;
2167 if (NULL != sr->probe_ctx)
2168 {
2169 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
2170 sr->probe_ctx = NULL;
2171 GNUNET_FS_stop_probe_ping_task_ (sr);
2172 }
2173 return dc;
2174}
2175
2176
2177/**
2178 * Start the downloading process (by entering the queue).
2179 *
2180 * @param dc our download context
2181 */
2182void
2183GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
2184{
2185 if (dc->completed == dc->length)
2186 return;
2187 if (NULL != dc->mq)
2188 return; /* already running */
2189 GNUNET_assert (NULL == dc->job_queue);
2190 GNUNET_assert (NULL == dc->task);
2191 GNUNET_assert (NULL != dc->active);
2192 dc->job_queue =
2193 GNUNET_FS_queue_ (dc->h,
2194 &activate_fs_download,
2195 &deactivate_fs_download,
2196 dc,
2197 (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE,
2198 (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
2199 ? GNUNET_FS_QUEUE_PRIORITY_NORMAL
2200 : GNUNET_FS_QUEUE_PRIORITY_PROBE);
2201 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2202 "Download %p put into queue as job %p\n",
2203 dc,
2204 dc->job_queue);
2205}
2206
2207
2208/**
2209 * Suspend a download.
2210 *
2211 * @param dc handle for the download
2212 */
2213void
2214GNUNET_FS_download_suspend (struct GNUNET_FS_DownloadContext *dc)
2215{
2216 deactivate_fs_download (dc);
2217}
2218
2219
2220/**
2221 * Resume a suspended download.
2222 *
2223 * @param dc handle for the download
2224 */
2225void
2226GNUNET_FS_download_resume (struct GNUNET_FS_DownloadContext *dc)
2227{
2228 struct GNUNET_FS_ProgressInfo pi;
2229
2230 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
2231 GNUNET_FS_download_make_status_ (&pi, dc);
2232
2233 GNUNET_assert (NULL == dc->task);
2234 dc->job_queue =
2235 GNUNET_FS_queue_ (dc->h,
2236 &activate_fs_download,
2237 &deactivate_fs_download,
2238 dc,
2239 (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE,
2240 (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
2241 ? GNUNET_FS_QUEUE_PRIORITY_NORMAL
2242 : GNUNET_FS_QUEUE_PRIORITY_PROBE);
2243}
2244
2245
2246/**
2247 * Stop a download (aborts if download is incomplete).
2248 *
2249 * @param dc handle for the download
2250 * @param do_delete delete files of incomplete downloads
2251 */
2252void
2253GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc, int do_delete)
2254{
2255 struct GNUNET_FS_ProgressInfo pi;
2256 int have_children;
2257 int search_was_null;
2258
2259 if (NULL != dc->top)
2260 GNUNET_FS_end_top (dc->h, dc->top);
2261 if (NULL != dc->task)
2262 {
2263 GNUNET_SCHEDULER_cancel (dc->task);
2264 dc->task = NULL;
2265 }
2266 search_was_null = (NULL == dc->search);
2267 if (NULL != dc->search)
2268 {
2269 dc->search->download = NULL;
2270 GNUNET_FS_search_result_sync_ (dc->search);
2271 dc->search = NULL;
2272 }
2273 if (NULL != dc->job_queue)
2274 {
2275 GNUNET_FS_dequeue_ (dc->job_queue);
2276 dc->job_queue = NULL;
2277 }
2278 if (NULL != dc->te)
2279 {
2280 GNUNET_FS_tree_encoder_finish (dc->te, NULL);
2281 dc->te = NULL;
2282 }
2283 have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
2284 while (NULL != dc->child_head)
2285 GNUNET_FS_download_stop (dc->child_head, do_delete);
2286 if (NULL != dc->parent)
2287 GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2288 dc->parent->child_tail,
2289 dc);
2290 if (NULL != dc->serialization)
2291 GNUNET_FS_remove_sync_file_ (dc->h,
2292 ((NULL != dc->parent) || (! search_was_null))
2293 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2294 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2295 dc->serialization);
2296 if ((GNUNET_YES == have_children) && (NULL == dc->parent))
2297 GNUNET_FS_remove_sync_dir_ (dc->h,
2298 (! search_was_null)
2299 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2300 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2301 dc->serialization);
2302 pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
2303 GNUNET_FS_download_make_status_ (&pi, dc);
2304 GNUNET_FS_free_download_request_ (dc->top_request);
2305 dc->top_request = NULL;
2306 if (NULL != dc->active)
2307 {
2308 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2309 dc->active = NULL;
2310 }
2311 if (NULL != dc->filename)
2312 {
2313 if ((dc->completed != dc->length) && (GNUNET_YES == do_delete))
2314 {
2315 if ((0 != unlink (dc->filename)) && (ENOENT != errno))
2316 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2317 "unlink",
2318 dc->filename);
2319 }
2320 GNUNET_free (dc->filename);
2321 }
2322 GNUNET_FS_meta_data_destroy (dc->meta);
2323 GNUNET_FS_uri_destroy (dc->uri);
2324 if (NULL != dc->temp_filename)
2325 {
2326 if (0 != unlink (dc->temp_filename))
2327 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2328 "unlink",
2329 dc->temp_filename);
2330 GNUNET_free (dc->temp_filename);
2331 }
2332 GNUNET_free (dc->serialization);
2333 GNUNET_assert (NULL == dc->job_queue);
2334 GNUNET_free (dc);
2335}
2336
2337
2338/* end of fs_download.c */
diff --git a/src/service/fs/fs_file_information.c b/src/service/fs/fs_file_information.c
new file mode 100644
index 000000000..f23b9da2a
--- /dev/null
+++ b/src/service/fs/fs_file_information.c
@@ -0,0 +1,407 @@
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/service/fs/fs_getopt.c b/src/service/fs/fs_getopt.c
new file mode 100644
index 000000000..0135e2e05
--- /dev/null
+++ b/src/service/fs/fs_getopt.c
@@ -0,0 +1,274 @@
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/service/fs/fs_list_indexed.c b/src/service/fs/fs_list_indexed.c
new file mode 100644
index 000000000..78816cad1
--- /dev/null
+++ b/src/service/fs/fs_list_indexed.c
@@ -0,0 +1,219 @@
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/service/fs/fs_misc.c b/src/service/fs/fs_misc.c
new file mode 100644
index 000000000..a8e23f042
--- /dev/null
+++ b/src/service/fs/fs_misc.c
@@ -0,0 +1,165 @@
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/service/fs/fs_namespace.c b/src/service/fs/fs_namespace.c
new file mode 100644
index 000000000..f8b7b91c0
--- /dev/null
+++ b/src/service/fs/fs_namespace.c
@@ -0,0 +1,804 @@
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/service/fs/fs_publish.c b/src/service/fs/fs_publish.c
new file mode 100644
index 000000000..d1662c78b
--- /dev/null
+++ b/src/service/fs/fs_publish.c
@@ -0,0 +1,1625 @@
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/service/fs/fs_publish_ksk.c b/src/service/fs/fs_publish_ksk.c
new file mode 100644
index 000000000..3981ad335
--- /dev/null
+++ b/src/service/fs/fs_publish_ksk.c
@@ -0,0 +1,255 @@
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/service/fs/fs_publish_ublock.c b/src/service/fs/fs_publish_ublock.c
new file mode 100644
index 000000000..ad12d9b08
--- /dev/null
+++ b/src/service/fs/fs_publish_ublock.c
@@ -0,0 +1,299 @@
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/service/fs/fs_publish_ublock.h b/src/service/fs/fs_publish_ublock.h
new file mode 100644
index 000000000..4adffc6c1
--- /dev/null
+++ b/src/service/fs/fs_publish_ublock.h
@@ -0,0 +1,109 @@
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/service/fs/fs_search.c b/src/service/fs/fs_search.c
new file mode 100644
index 000000000..8b8c54c67
--- /dev/null
+++ b/src/service/fs/fs_search.c
@@ -0,0 +1,1826 @@
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/service/fs/fs_sharetree.c b/src/service/fs/fs_sharetree.c
new file mode 100644
index 000000000..6c246a3ad
--- /dev/null
+++ b/src/service/fs/fs_sharetree.c
@@ -0,0 +1,457 @@
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/service/fs/fs_tree.c b/src/service/fs/fs_tree.c
new file mode 100644
index 000000000..65f589966
--- /dev/null
+++ b/src/service/fs/fs_tree.c
@@ -0,0 +1,452 @@
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/service/fs/fs_tree.h b/src/service/fs/fs_tree.h
new file mode 100644
index 000000000..1fb681d27
--- /dev/null
+++ b/src/service/fs/fs_tree.h
@@ -0,0 +1,217 @@
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/service/fs/fs_unindex.c b/src/service/fs/fs_unindex.c
new file mode 100644
index 000000000..68ba667c4
--- /dev/null
+++ b/src/service/fs/fs_unindex.c
@@ -0,0 +1,902 @@
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/service/fs/fs_uri.c b/src/service/fs/fs_uri.c
new file mode 100644
index 000000000..40ec76a03
--- /dev/null
+++ b/src/service/fs/fs_uri.c
@@ -0,0 +1,2062 @@
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 * What is the hash of the original file's content
1379 * that this URI refers to?
1380 *
1381 * @param uri the CHK URI to inspect
1382 * @return hash of the file as specified in the CHK URI
1383 */
1384const struct GNUNET_HashCode*
1385GNUNET_FS_uri_chk_get_file_hash (const struct GNUNET_FS_Uri *uri)
1386{
1387 if (GNUNET_FS_URI_CHK != uri->type)
1388 GNUNET_assert (0);
1389
1390 return &(uri->data.chk.chk.key);
1391}
1392
1393
1394/**
1395 * Is this a location URI?
1396 *
1397 * @param uri the uri to check
1398 * @return #GNUNET_YES if this is a LOC uri
1399 */
1400int
1401GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri)
1402{
1403 return uri->type == GNUNET_FS_URI_LOC;
1404}
1405
1406
1407/**
1408 * Add a keyword as non-mandatory (with ' '-prefix) to the
1409 * given keyword list at offset 'index'. The array is
1410 * guaranteed to be long enough.
1411 *
1412 * @param s keyword to add
1413 * @param array array to add the keyword to
1414 * @param index offset where to add the keyword
1415 */
1416static void
1417insert_non_mandatory_keyword (const char *s, char **array, int index)
1418{
1419 char *nkword;
1420
1421 GNUNET_asprintf (&nkword,
1422 " %s", /* space to mark as 'non mandatory' */
1423 s);
1424 array[index] = nkword;
1425}
1426
1427
1428/**
1429 * Test if the given keyword @a s is already present in the
1430 * given array, ignoring the '+'-mandatory prefix in the array.
1431 *
1432 * @param s keyword to test
1433 * @param array keywords to test against, with ' ' or '+' prefix to ignore
1434 * @param array_length length of the @a array
1435 * @return #GNUNET_YES if the keyword exists, #GNUNET_NO if not
1436 */
1437static int
1438find_duplicate (const char *s, const char **array, int array_length)
1439{
1440 int j;
1441
1442 for (j = array_length - 1; j >= 0; j--)
1443 if (0 == strcmp (&array[j][1], s))
1444 return GNUNET_YES;
1445 return GNUNET_NO;
1446}
1447
1448
1449/**
1450 * FIXME: comment
1451 */
1452static char *
1453normalize_metadata (enum EXTRACTOR_MetaFormat format,
1454 const char *data,
1455 size_t data_len)
1456{
1457 uint8_t *free_str = NULL;
1458 uint8_t *str_to_normalize = (uint8_t *) data;
1459 uint8_t *normalized;
1460 size_t r_len;
1461
1462 if (str_to_normalize == NULL)
1463 return NULL;
1464 /* Don't trust libextractor */
1465 if (format == EXTRACTOR_METAFORMAT_UTF8)
1466 {
1467 free_str = (uint8_t *) u8_check ((const uint8_t *) data, data_len);
1468 if (free_str == NULL)
1469 free_str = NULL;
1470 else
1471 format = EXTRACTOR_METAFORMAT_C_STRING;
1472 }
1473 if (format == EXTRACTOR_METAFORMAT_C_STRING)
1474 {
1475 free_str = u8_strconv_from_encoding (data,
1476 locale_charset (),
1477 iconveh_escape_sequence);
1478 if (free_str == NULL)
1479 return NULL;
1480 }
1481
1482 normalized = u8_tolower (str_to_normalize,
1483 strlen ((char *) str_to_normalize),
1484 NULL,
1485 UNINORM_NFD,
1486 NULL,
1487 &r_len);
1488 /* free_str is allocated by libunistring internally, use free() */
1489 if (free_str != NULL)
1490 free (free_str);
1491 if (normalized != NULL)
1492 {
1493 /* u8_tolower allocates a non-NULL-terminated string! */
1494 free_str = GNUNET_malloc (r_len + 1);
1495 GNUNET_memcpy (free_str, normalized, r_len);
1496 free_str[r_len] = '\0';
1497 free (normalized);
1498 normalized = free_str;
1499 }
1500 return (char *) normalized;
1501}
1502
1503
1504/**
1505 * Counts the number of UTF-8 characters (not bytes) in the string,
1506 * returns that count.
1507 */
1508static size_t
1509u8_strcount (const uint8_t *s)
1510{
1511 size_t count;
1512 ucs4_t c;
1513
1514 GNUNET_assert (s != NULL);
1515 if (s[0] == 0)
1516 return 0;
1517 for (count = 0; s != NULL; count++)
1518 s = u8_next (&c, s);
1519 return count - 1;
1520}
1521
1522
1523/**
1524 * Break the filename up by matching [], () and {} pairs to make
1525 * keywords. In case of nesting parentheses only the inner pair counts.
1526 * You can't escape parentheses to scan something like "[blah\{foo]" to
1527 * make a "blah{foo" keyword, this function is only a heuristic!
1528 *
1529 * @param s string to break down.
1530 * @param array array to fill with enclosed tokens. If NULL, then tokens
1531 * are only counted.
1532 * @param index index at which to start filling the array (entries prior
1533 * to it are used to check for duplicates). ignored if @a array == NULL.
1534 * @return number of tokens counted (including duplicates), or number of
1535 * tokens extracted (excluding duplicates). 0 if there are no
1536 * matching parens in the string (when counting), or when all tokens
1537 * were duplicates (when extracting).
1538 */
1539static int
1540get_keywords_from_parens (const char *s, char **array, int index)
1541{
1542 int count = 0;
1543 char *open_paren;
1544 char *close_paren;
1545 char *ss;
1546 char tmp;
1547
1548 if (NULL == s)
1549 return 0;
1550 ss = GNUNET_strdup (s);
1551 open_paren = ss - 1;
1552 while (NULL != (open_paren = strpbrk (open_paren + 1, "[{(")))
1553 {
1554 int match = 0;
1555
1556 close_paren = strpbrk (open_paren + 1, "]})");
1557 if (NULL == close_paren)
1558 continue;
1559 switch (open_paren[0])
1560 {
1561 case '[':
1562 if (']' == close_paren[0])
1563 match = 1;
1564 break;
1565
1566 case '{':
1567 if ('}' == close_paren[0])
1568 match = 1;
1569 break;
1570
1571 case '(':
1572 if (')' == close_paren[0])
1573 match = 1;
1574 break;
1575
1576 default:
1577 break;
1578 }
1579 if (match && (close_paren - open_paren > 1))
1580 {
1581 tmp = close_paren[0];
1582 close_paren[0] = '\0';
1583 /* Keywords must be at least 3 characters long */
1584 if (u8_strcount ((const uint8_t *) &open_paren[1]) <= 2)
1585 {
1586 close_paren[0] = tmp;
1587 continue;
1588 }
1589 if (NULL != array)
1590 {
1591 char *normalized;
1592 if (GNUNET_NO == find_duplicate ((const char *) &open_paren[1],
1593 (const char **) array,
1594 index + count))
1595 {
1596 insert_non_mandatory_keyword ((const char *) &open_paren[1],
1597 array,
1598 index + count);
1599 count++;
1600 }
1601 normalized = normalize_metadata (EXTRACTOR_METAFORMAT_UTF8,
1602 &open_paren[1],
1603 close_paren - &open_paren[1]);
1604 if (normalized != NULL)
1605 {
1606 if (GNUNET_NO == find_duplicate ((const char *) normalized,
1607 (const char **) array,
1608 index + count))
1609 {
1610 insert_non_mandatory_keyword ((const char *) normalized,
1611 array,
1612 index + count);
1613 count++;
1614 }
1615 GNUNET_free (normalized);
1616 }
1617 }
1618 else
1619 count++;
1620 close_paren[0] = tmp;
1621 }
1622 }
1623 GNUNET_free (ss);
1624 return count;
1625}
1626
1627
1628/**
1629 * Where to break up keywords
1630 */
1631#define TOKENS "_. /-!?#&+@\"\'\\;:,()[]{}$<>|"
1632
1633/**
1634 * Break the filename up by TOKENS to make
1635 * keywords.
1636 *
1637 * @param s string to break down.
1638 * @param array array to fill with tokens. If NULL, then tokens are only
1639 * counted.
1640 * @param index index at which to start filling the array (entries prior
1641 * to it are used to check for duplicates). ignored if @a array == NULL.
1642 * @return number of tokens (>1) counted (including duplicates), or number of
1643 * tokens extracted (excluding duplicates). 0 if there are no
1644 * separators in the string (when counting), or when all tokens were
1645 * duplicates (when extracting).
1646 */
1647static int
1648get_keywords_from_tokens (const char *s, char **array, int index)
1649{
1650 char *p;
1651 char *ss;
1652 int seps = 0;
1653
1654 ss = GNUNET_strdup (s);
1655 for (p = strtok (ss, TOKENS); p != NULL; p = strtok (NULL, TOKENS))
1656 {
1657 /* Keywords must be at least 3 characters long */
1658 if (u8_strcount ((const uint8_t *) p) <= 2)
1659 continue;
1660 if (NULL != array)
1661 {
1662 char *normalized;
1663 if (GNUNET_NO == find_duplicate (p, (const char **) array, index + seps))
1664 {
1665 insert_non_mandatory_keyword (p, array, index + seps);
1666 seps++;
1667 }
1668 normalized =
1669 normalize_metadata (EXTRACTOR_METAFORMAT_UTF8, p, strlen (p));
1670 if (normalized != NULL)
1671 {
1672 if (GNUNET_NO == find_duplicate ((const char *) normalized,
1673 (const char **) array,
1674 index + seps))
1675 {
1676 insert_non_mandatory_keyword ((const char *) normalized,
1677 array,
1678 index + seps);
1679 seps++;
1680 }
1681 GNUNET_free (normalized);
1682 }
1683 }
1684 else
1685 seps++;
1686 }
1687 GNUNET_free (ss);
1688 return seps;
1689}
1690
1691
1692#undef TOKENS
1693
1694
1695/**
1696 * Function called on each value in the meta data.
1697 * Adds it to the URI.
1698 *
1699 * @param cls URI to update
1700 * @param plugin_name name of the plugin that produced this value;
1701 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
1702 * used in the main libextractor library and yielding
1703 * meta data).
1704 * @param type libextractor-type describing the meta data
1705 * @param format basic format information about data
1706 * @param data_mime_type mime-type of data (not of the original file);
1707 * can be NULL (if mime-type is not known)
1708 * @param data actual meta-data found
1709 * @param data_len number of bytes in @a data
1710 * @return 0 (always)
1711 */
1712static int
1713gather_uri_data (void *cls,
1714 const char *plugin_name,
1715 enum EXTRACTOR_MetaType type,
1716 enum EXTRACTOR_MetaFormat format,
1717 const char *data_mime_type,
1718 const char *data,
1719 size_t data_len)
1720{
1721 struct GNUNET_FS_Uri *uri = cls;
1722 char *normalized_data;
1723 const char *sep;
1724
1725 if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
1726 (format != EXTRACTOR_METAFORMAT_C_STRING))
1727 return 0;
1728 /* Keywords must be at least 3 characters long
1729 * If given non-utf8 string it will, most likely, find it to be invalid,
1730 * and will return the length of its valid part, skipping the keyword.
1731 * If it does - fix the extractor, not this check!
1732 */if (u8_strcount ((const uint8_t *) data) <= 2)
1733 return 0;
1734 if ((EXTRACTOR_METATYPE_MIMETYPE == type) &&
1735 (NULL != (sep = memchr (data, '/', data_len))) && (sep != data))
1736 {
1737 char *xtra;
1738
1739 GNUNET_asprintf (&xtra, "mimetype:%.*s", (int) (sep - data), data);
1740 if (! find_duplicate (xtra,
1741 (const char **) uri->data.ksk.keywords,
1742 uri->data.ksk.keywordCount))
1743 {
1744 insert_non_mandatory_keyword (xtra,
1745 uri->data.ksk.keywords,
1746 uri->data.ksk.keywordCount);
1747 uri->data.ksk.keywordCount++;
1748 }
1749 GNUNET_free (xtra);
1750 }
1751
1752 normalized_data = normalize_metadata (format, data, data_len);
1753 if (! find_duplicate (data,
1754 (const char **) uri->data.ksk.keywords,
1755 uri->data.ksk.keywordCount))
1756 {
1757 insert_non_mandatory_keyword (data,
1758 uri->data.ksk.keywords,
1759 uri->data.ksk.keywordCount);
1760 uri->data.ksk.keywordCount++;
1761 }
1762 if (NULL != normalized_data)
1763 {
1764 if (! find_duplicate (normalized_data,
1765 (const char **) uri->data.ksk.keywords,
1766 uri->data.ksk.keywordCount))
1767 {
1768 insert_non_mandatory_keyword (normalized_data,
1769 uri->data.ksk.keywords,
1770 uri->data.ksk.keywordCount);
1771 uri->data.ksk.keywordCount++;
1772 }
1773 GNUNET_free (normalized_data);
1774 }
1775 return 0;
1776}
1777
1778
1779/**
1780 * Construct a keyword-URI from meta-data (take all entries
1781 * in the meta-data and construct one large keyword URI
1782 * that lists all keywords that can be found in the meta-data).
1783 *
1784 * @param md metadata to use
1785 * @return NULL on error, otherwise a KSK URI
1786 */
1787struct GNUNET_FS_Uri *
1788GNUNET_FS_uri_ksk_create_from_meta_data (
1789 const struct GNUNET_FS_MetaData *md)
1790{
1791 struct GNUNET_FS_Uri *ret;
1792 char *filename;
1793 char *full_name = NULL;
1794 char *ss;
1795 int ent;
1796 int tok_keywords = 0;
1797 int paren_keywords = 0;
1798
1799 if (NULL == md)
1800 return NULL;
1801 ret = GNUNET_new (struct GNUNET_FS_Uri);
1802 ret->type = GNUNET_FS_URI_KSK;
1803 ent = GNUNET_FS_meta_data_iterate (md, NULL, NULL);
1804 if (ent > 0)
1805 {
1806 full_name = GNUNET_FS_meta_data_get_first_by_types (
1807 md,
1808 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
1809 -1);
1810 if (NULL != full_name)
1811 {
1812 filename = full_name;
1813 while (NULL != (ss = strstr (filename, DIR_SEPARATOR_STR)))
1814 filename = ss + 1;
1815 tok_keywords = get_keywords_from_tokens (filename, NULL, 0);
1816 paren_keywords = get_keywords_from_parens (filename, NULL, 0);
1817 }
1818 /* x3 because there might be a normalized variant of every keyword,
1819 plus theoretically one more for mime... */
1820 ret->data.ksk.keywords =
1821 GNUNET_new_array ((ent + tok_keywords + paren_keywords) * 3, char *);
1822 GNUNET_FS_meta_data_iterate (md, &gather_uri_data, ret);
1823 }
1824 if (tok_keywords > 0)
1825 ret->data.ksk.keywordCount +=
1826 get_keywords_from_tokens (filename,
1827 ret->data.ksk.keywords,
1828 ret->data.ksk.keywordCount);
1829 if (paren_keywords > 0)
1830 ret->data.ksk.keywordCount +=
1831 get_keywords_from_parens (filename,
1832 ret->data.ksk.keywords,
1833 ret->data.ksk.keywordCount);
1834 if (ent > 0)
1835 GNUNET_free (full_name);
1836 return ret;
1837}
1838
1839
1840/**
1841 * In URI-encoding, does the given character
1842 * need to be encoded using %-encoding?
1843 */
1844static int
1845needs_percent (char c)
1846{
1847 return(! ((isalnum ((unsigned char) c)) || (c == '-') || (c == '_') ||
1848 (c == '.') || (c == '~')));
1849}
1850
1851
1852/**
1853 * Convert a KSK URI to a string.
1854 *
1855 * @param uri the URI to convert
1856 * @return NULL on error (i.e. keywordCount == 0)
1857 */
1858static char *
1859uri_ksk_to_string (const struct GNUNET_FS_Uri *uri)
1860{
1861 char **keywords;
1862 unsigned int keywordCount;
1863 size_t n;
1864 char *ret;
1865 unsigned int i;
1866 unsigned int j;
1867 unsigned int wpos;
1868 size_t slen;
1869 const char *keyword;
1870
1871 if (uri->type != GNUNET_FS_URI_KSK)
1872 return NULL;
1873 keywords = uri->data.ksk.keywords;
1874 keywordCount = uri->data.ksk.keywordCount;
1875 n = keywordCount + strlen (GNUNET_FS_URI_PREFIX)
1876 + strlen (GNUNET_FS_URI_KSK_INFIX) + 1;
1877 for (i = 0; i < keywordCount; i++)
1878 {
1879 keyword = keywords[i];
1880 slen = strlen (keyword);
1881 n += slen;
1882 for (j = 0; j < slen; j++)
1883 {
1884 if ((j == 0) && (keyword[j] == ' '))
1885 {
1886 n--;
1887 continue; /* skip leading space */
1888 }
1889 if (needs_percent (keyword[j]))
1890 n += 2; /* will use %-encoding */
1891 }
1892 }
1893 ret = GNUNET_malloc (n);
1894 strcpy (ret, GNUNET_FS_URI_PREFIX);
1895 strcat (ret, GNUNET_FS_URI_KSK_INFIX);
1896 wpos = strlen (ret);
1897 for (i = 0; i < keywordCount; i++)
1898 {
1899 keyword = keywords[i];
1900 slen = strlen (keyword);
1901 for (j = 0; j < slen; j++)
1902 {
1903 if ((j == 0) && (keyword[j] == ' '))
1904 continue; /* skip leading space */
1905 if (needs_percent (keyword[j]))
1906 {
1907 sprintf (&ret[wpos], "%%%02X", (unsigned char) keyword[j]);
1908 wpos += 3;
1909 }
1910 else
1911 {
1912 ret[wpos++] = keyword[j];
1913 }
1914 }
1915 if (i != keywordCount - 1)
1916 ret[wpos++] = '+';
1917 }
1918 return ret;
1919}
1920
1921
1922/**
1923 * Convert SKS URI to a string.
1924 *
1925 * @param uri sks uri to convert
1926 * @return NULL on error
1927 */
1928static char *
1929uri_sks_to_string (const struct GNUNET_FS_Uri *uri)
1930{
1931 char *ret;
1932 char buf[1024];
1933
1934 if (GNUNET_FS_URI_SKS != uri->type)
1935 return NULL;
1936 ret =
1937 GNUNET_STRINGS_data_to_string (&uri->data.sks.ns,
1938 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
1939 buf,
1940 sizeof(buf));
1941 GNUNET_assert (NULL != ret);
1942 ret[0] = '\0';
1943 GNUNET_asprintf (&ret,
1944 "%s%s%s/%s",
1945 GNUNET_FS_URI_PREFIX,
1946 GNUNET_FS_URI_SKS_INFIX,
1947 buf,
1948 uri->data.sks.identifier);
1949 return ret;
1950}
1951
1952
1953/**
1954 * Convert a CHK URI to a string.
1955 *
1956 * @param uri chk uri to convert
1957 * @return NULL on error
1958 */
1959static char *
1960uri_chk_to_string (const struct GNUNET_FS_Uri *uri)
1961{
1962 const struct FileIdentifier *fi;
1963 char *ret;
1964 struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1965 struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1966
1967 if (uri->type != GNUNET_FS_URI_CHK)
1968 return NULL;
1969 fi = &uri->data.chk;
1970 GNUNET_CRYPTO_hash_to_enc (&fi->chk.key, &keyhash);
1971 GNUNET_CRYPTO_hash_to_enc (&fi->chk.query, &queryhash);
1972
1973 GNUNET_asprintf (&ret,
1974 "%s%s%s.%s.%llu",
1975 GNUNET_FS_URI_PREFIX,
1976 GNUNET_FS_URI_CHK_INFIX,
1977 (const char *) &keyhash,
1978 (const char *) &queryhash,
1979 (unsigned long long) GNUNET_ntohll (fi->file_length));
1980 return ret;
1981}
1982
1983
1984/**
1985 * Convert a LOC URI to a string.
1986 *
1987 * @param uri loc uri to convert
1988 * @return NULL on error
1989 */
1990static char *
1991uri_loc_to_string (const struct GNUNET_FS_Uri *uri)
1992{
1993 char *ret;
1994 struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1995 struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1996 char *peer_id;
1997 char peer_sig[SIGNATURE_ASCII_LENGTH + 1];
1998
1999 GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.key, &keyhash);
2000 GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.query, &queryhash);
2001 peer_id =
2002 GNUNET_CRYPTO_eddsa_public_key_to_string (&uri->data.loc.peer.public_key);
2003 GNUNET_assert (
2004 NULL !=
2005 GNUNET_STRINGS_data_to_string (&uri->data.loc.contentSignature,
2006 sizeof(struct GNUNET_CRYPTO_EddsaSignature),
2007 peer_sig,
2008 sizeof(peer_sig)));
2009 GNUNET_asprintf (&ret,
2010 "%s%s%s.%s.%llu.%s.%s.%llu",
2011 GNUNET_FS_URI_PREFIX,
2012 GNUNET_FS_URI_LOC_INFIX,
2013 (const char *) &keyhash,
2014 (const char *) &queryhash,
2015 (unsigned long long) GNUNET_ntohll (
2016 uri->data.loc.fi.file_length),
2017 peer_id,
2018 peer_sig,
2019 (unsigned long long)
2020 uri->data.loc.expirationTime.abs_value_us
2021 / 1000000LL);
2022 GNUNET_free (peer_id);
2023 return ret;
2024}
2025
2026
2027/**
2028 * Convert a URI to a UTF-8 String.
2029 *
2030 * @param uri uri to convert to a string
2031 * @return the UTF-8 string
2032 */
2033char *
2034GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri)
2035{
2036 if (uri == NULL)
2037 {
2038 GNUNET_break (0);
2039 return NULL;
2040 }
2041 switch (uri->type)
2042 {
2043 case GNUNET_FS_URI_KSK:
2044 return uri_ksk_to_string (uri);
2045
2046 case GNUNET_FS_URI_SKS:
2047 return uri_sks_to_string (uri);
2048
2049 case GNUNET_FS_URI_CHK:
2050 return uri_chk_to_string (uri);
2051
2052 case GNUNET_FS_URI_LOC:
2053 return uri_loc_to_string (uri);
2054
2055 default:
2056 GNUNET_break (0);
2057 return NULL;
2058 }
2059}
2060
2061
2062/* end of fs_uri.c */
diff --git a/src/service/fs/gnunet-daemon-fsprofiler.c b/src/service/fs/gnunet-daemon-fsprofiler.c
new file mode 100644
index 000000000..b99933cfa
--- /dev/null
+++ b/src/service/fs/gnunet-daemon-fsprofiler.c
@@ -0,0 +1,673 @@
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/service/fs/gnunet-fs-profiler.c b/src/service/fs/gnunet-fs-profiler.c
new file mode 100644
index 000000000..62da46834
--- /dev/null
+++ b/src/service/fs/gnunet-fs-profiler.c
@@ -0,0 +1,245 @@
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/service/fs/gnunet-helper-fs-publish.c b/src/service/fs/gnunet-helper-fs-publish.c
new file mode 100644
index 000000000..0e07b79dc
--- /dev/null
+++ b/src/service/fs/gnunet-helper-fs-publish.c
@@ -0,0 +1,579 @@
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/service/fs/gnunet-service-fs.c b/src/service/fs/gnunet-service-fs.c
new file mode 100644
index 000000000..1ab6ac2b8
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs.c
@@ -0,0 +1,1378 @@
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/service/fs/gnunet-service-fs.h b/src/service/fs/gnunet-service-fs.h
new file mode 100644
index 000000000..e102a1fba
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs.h
@@ -0,0 +1,304 @@
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/service/fs/gnunet-service-fs_cadet.h b/src/service/fs/gnunet-service-fs_cadet.h
new file mode 100644
index 000000000..c02021a0d
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_cadet.h
@@ -0,0 +1,168 @@
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/service/fs/gnunet-service-fs_cadet_client.c b/src/service/fs/gnunet-service-fs_cadet_client.c
new file mode 100644
index 000000000..398fcd604
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_cadet_client.c
@@ -0,0 +1,728 @@
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/service/fs/gnunet-service-fs_cadet_server.c b/src/service/fs/gnunet-service-fs_cadet_server.c
new file mode 100644
index 000000000..8bfe91cf0
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_cadet_server.c
@@ -0,0 +1,545 @@
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/service/fs/gnunet-service-fs_cp.c b/src/service/fs/gnunet-service-fs_cp.c
new file mode 100644
index 000000000..df934feaf
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_cp.c
@@ -0,0 +1,1667 @@
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) && (NULL == emsg))
398 {
399 cp->respect_iterate_req = NULL;
400 return;
401 }
402 if ((NULL != record) &&
403 (sizeof(cp->disk_respect) == record->value_size))
404 {
405 cp->disk_respect = *((uint32_t *) record->value);
406 cp->ppd.respect += *((uint32_t *) record->value);
407 }
408 GSF_push_start_ (cp);
409 if (NULL != record)
410 {
411 GNUNET_PEERSTORE_iteration_stop (cp->respect_iterate_req);
412 cp->respect_iterate_req = NULL;
413 return;
414 }
415 GNUNET_PEERSTORE_iteration_next (cp->respect_iterate_req, 1);
416}
417
418
419/**
420 * Function called for each pending request whenever a new
421 * peer connects, giving us a chance to decide about submitting
422 * the existing request to the new peer.
423 *
424 * @param cls the `struct GSF_ConnectedPeer` of the new peer
425 * @param key query for the request
426 * @param pr handle to the pending request
427 * @return #GNUNET_YES to continue to iterate
428 */
429static int
430consider_peer_for_forwarding (void *cls,
431 const struct GNUNET_HashCode *key,
432 struct GSF_PendingRequest *pr)
433{
434 struct GSF_ConnectedPeer *cp = cls;
435 struct GNUNET_PeerIdentity pid;
436
437 if (GNUNET_YES !=
438 GSF_pending_request_test_active_ (pr))
439 return GNUNET_YES; /* request is not actually active, skip! */
440 GSF_connected_peer_get_identity_ (cp, &pid);
441 if (GNUNET_YES !=
442 GSF_pending_request_test_target_ (pr, &pid))
443 {
444 GNUNET_STATISTICS_update (GSF_stats,
445 gettext_noop ("# Loopback routes suppressed"),
446 1,
447 GNUNET_NO);
448 return GNUNET_YES;
449 }
450 GSF_plan_add_ (cp, pr);
451 return GNUNET_YES;
452}
453
454
455void *
456GSF_peer_connect_handler (void *cls,
457 const struct GNUNET_PeerIdentity *peer,
458 struct GNUNET_MQ_Handle *mq)
459{
460 struct GSF_ConnectedPeer *cp;
461
462 if (0 ==
463 GNUNET_memcmp (&GSF_my_id,
464 peer))
465 return NULL;
466 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
467 "Connected to peer %s\n",
468 GNUNET_i2s (peer));
469 cp = GNUNET_new (struct GSF_ConnectedPeer);
470 cp->ppd.pid = GNUNET_PEER_intern (peer);
471 cp->ppd.peer = peer;
472 cp->mq = mq;
473 cp->ppd.transmission_delay = GNUNET_LOAD_value_init (GNUNET_TIME_UNIT_ZERO);
474
475 cp->request_map = GNUNET_CONTAINER_multihashmap_create (128,
476 GNUNET_YES);
477 GNUNET_break (GNUNET_OK ==
478 GNUNET_CONTAINER_multipeermap_put (cp_map,
479 GSF_connected_peer_get_identity2_ (
480 cp),
481 cp,
482 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
483 GNUNET_STATISTICS_set (GSF_stats,
484 gettext_noop ("# peers connected"),
485 GNUNET_CONTAINER_multipeermap_size (cp_map),
486 GNUNET_NO);
487 cp->respect_iterate_req
488 = GNUNET_PEERSTORE_iteration_start (peerstore,
489 "fs",
490 peer,
491 "respect",
492 &peer_respect_cb,
493 cp);
494 GSF_iterate_pending_requests_ (&consider_peer_for_forwarding,
495 cp);
496 return cp;
497}
498
499
500/**
501 * It may be time to re-start migrating content to this
502 * peer. Check, and if so, restart migration.
503 *
504 * @param cls the `struct GSF_ConnectedPeer`
505 */
506static void
507revive_migration (void *cls)
508{
509 struct GSF_ConnectedPeer *cp = cls;
510 struct GNUNET_TIME_Relative bt;
511
512 cp->mig_revive_task = NULL;
513 bt = GNUNET_TIME_absolute_get_remaining (cp->ppd.migration_blocked_until);
514 if (0 != bt.rel_value_us)
515 {
516 /* still time left... */
517 cp->mig_revive_task =
518 GNUNET_SCHEDULER_add_delayed (bt, &revive_migration, cp);
519 return;
520 }
521 GSF_push_start_ (cp);
522}
523
524
525struct GSF_ConnectedPeer *
526GSF_peer_get_ (const struct GNUNET_PeerIdentity *peer)
527{
528 if (NULL == cp_map)
529 return NULL;
530 return GNUNET_CONTAINER_multipeermap_get (cp_map, peer);
531}
532
533
534/**
535 * Handle P2P #GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP message.
536 *
537 * @param cls closure, the `struct GSF_ConnectedPeer`
538 * @param msm the actual message
539 */
540void
541handle_p2p_migration_stop (void *cls,
542 const struct MigrationStopMessage *msm)
543{
544 struct GSF_ConnectedPeer *cp = cls;
545 struct GNUNET_TIME_Relative bt;
546
547 GNUNET_STATISTICS_update (GSF_stats,
548 gettext_noop ("# migration stop messages received"),
549 1, GNUNET_NO);
550 bt = GNUNET_TIME_relative_ntoh (msm->duration);
551 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
552 _ ("Migration of content to peer `%s' blocked for %s\n"),
553 GNUNET_i2s (cp->ppd.peer),
554 GNUNET_STRINGS_relative_time_to_string (bt, GNUNET_YES));
555 cp->ppd.migration_blocked_until = GNUNET_TIME_relative_to_absolute (bt);
556 if ((NULL == cp->mig_revive_task) &&
557 (NULL == cp->respect_iterate_req))
558 {
559 GSF_push_stop_ (cp);
560 cp->mig_revive_task =
561 GNUNET_SCHEDULER_add_delayed (bt,
562 &revive_migration, cp);
563 }
564}
565
566
567/**
568 * Free resources associated with the given peer request.
569 *
570 * @param peerreq request to free
571 */
572static void
573free_pending_request (struct PeerRequest *peerreq)
574{
575 struct GSF_ConnectedPeer *cp = peerreq->cp;
576 struct GSF_PendingRequestData *prd;
577
578 prd = GSF_pending_request_get_data_ (peerreq->pr);
579 if (NULL != peerreq->kill_task)
580 {
581 GNUNET_SCHEDULER_cancel (peerreq->kill_task);
582 peerreq->kill_task = NULL;
583 }
584 GNUNET_STATISTICS_update (GSF_stats,
585 gettext_noop ("# P2P searches active"),
586 -1,
587 GNUNET_NO);
588 GNUNET_break (GNUNET_YES ==
589 GNUNET_CONTAINER_multihashmap_remove (cp->request_map,
590 &prd->query,
591 peerreq));
592 GNUNET_free (peerreq);
593}
594
595
596/**
597 * Cancel all requests associated with the peer.
598 *
599 * @param cls unused
600 * @param query hash code of the request
601 * @param value the `struct GSF_PendingRequest`
602 * @return #GNUNET_YES (continue to iterate)
603 */
604static int
605cancel_pending_request (void *cls,
606 const struct GNUNET_HashCode *query,
607 void *value)
608{
609 struct PeerRequest *peerreq = value;
610 struct GSF_PendingRequest *pr = peerreq->pr;
611
612 free_pending_request (peerreq);
613 GSF_pending_request_cancel_ (pr,
614 GNUNET_NO);
615 return GNUNET_OK;
616}
617
618
619/**
620 * Free the given request.
621 *
622 * @param cls the request to free
623 */
624static void
625peer_request_destroy (void *cls)
626{
627 struct PeerRequest *peerreq = cls;
628 struct GSF_PendingRequest *pr = peerreq->pr;
629 struct GSF_PendingRequestData *prd;
630
631 peerreq->kill_task = NULL;
632 prd = GSF_pending_request_get_data_ (pr);
633 cancel_pending_request (NULL,
634 &prd->query,
635 peerreq);
636}
637
638
639/**
640 * The artificial delay is over, transmit the message now.
641 *
642 * @param cls the `struct GSF_DelayedHandle` with the message
643 */
644static void
645transmit_delayed_now (void *cls)
646{
647 struct GSF_DelayedHandle *dh = cls;
648 struct GSF_ConnectedPeer *cp = dh->cp;
649
650 GNUNET_CONTAINER_DLL_remove (cp->delayed_head,
651 cp->delayed_tail,
652 dh);
653 cp->delay_queue_size--;
654 GSF_peer_transmit_ (cp,
655 GNUNET_NO,
656 UINT32_MAX,
657 dh->env);
658 GNUNET_free (dh);
659}
660
661
662/**
663 * Get the randomized delay a response should be subjected to.
664 *
665 * @return desired delay
666 */
667static struct GNUNET_TIME_Relative
668get_randomized_delay ()
669{
670 struct GNUNET_TIME_Relative ret;
671
672 ret =
673 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
674 GNUNET_CRYPTO_random_u32
675 (GNUNET_CRYPTO_QUALITY_WEAK,
676 2 * GSF_avg_latency.rel_value_us + 1));
677#if INSANE_STATISTICS
678 GNUNET_STATISTICS_update (GSF_stats,
679 gettext_noop
680 ("# artificial delays introduced (ms)"),
681 ret.rel_value_us / 1000LL, GNUNET_NO);
682#endif
683 return ret;
684}
685
686
687/**
688 * Handle a reply to a pending request. Also called if a request
689 * expires (then with data == NULL). The handler may be called
690 * many times (depending on the request type), but will not be
691 * called during or after a call to GSF_pending_request_cancel
692 * and will also not be called anymore after a call signalling
693 * expiration.
694 *
695 * @param cls `struct PeerRequest` this is an answer for
696 * @param eval evaluation of the result
697 * @param pr handle to the original pending request
698 * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown"
699 * @param expiration when does @a data expire?
700 * @param last_transmission when did we last transmit a request for this block
701 * @param type type of the block
702 * @param data response data, NULL on request expiration
703 * @param data_len number of bytes in @a data
704 */
705static void
706handle_p2p_reply (void *cls,
707 enum GNUNET_BLOCK_ReplyEvaluationResult eval,
708 struct GSF_PendingRequest *pr,
709 uint32_t reply_anonymity_level,
710 struct GNUNET_TIME_Absolute expiration,
711 struct GNUNET_TIME_Absolute last_transmission,
712 enum GNUNET_BLOCK_Type type,
713 const void *data,
714 size_t data_len)
715{
716 struct PeerRequest *peerreq = cls;
717 struct GSF_ConnectedPeer *cp = peerreq->cp;
718 struct GSF_PendingRequestData *prd;
719 struct GNUNET_MQ_Envelope *env;
720 struct PutMessage *pm;
721 size_t msize;
722
723 GNUNET_assert (data_len + sizeof(struct PutMessage) <
724 GNUNET_MAX_MESSAGE_SIZE);
725 GNUNET_assert (peerreq->pr == pr);
726 prd = GSF_pending_request_get_data_ (pr);
727 if (NULL == data)
728 {
729 free_pending_request (peerreq);
730 return;
731 }
732 GNUNET_break (GNUNET_BLOCK_TYPE_ANY != type);
733 if ( (prd->type != type) &&
734 (GNUNET_BLOCK_TYPE_ANY != prd->type) )
735 {
736 GNUNET_STATISTICS_update (GSF_stats,
737 "# replies dropped due to type mismatch",
738 1, GNUNET_NO);
739 return;
740 }
741 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
742 "Transmitting result for query `%s' to peer\n",
743 GNUNET_h2s (&prd->query));
744 GNUNET_STATISTICS_update (GSF_stats,
745 "# replies received for other peers",
746 1,
747 GNUNET_NO);
748 msize = sizeof(struct PutMessage) + data_len;
749 if (msize >= GNUNET_MAX_MESSAGE_SIZE)
750 {
751 GNUNET_break (0);
752 return;
753 }
754 if ( (UINT32_MAX != reply_anonymity_level) &&
755 (reply_anonymity_level > 1) )
756 {
757 if (reply_anonymity_level - 1 > GSF_cover_content_count)
758 {
759 GNUNET_STATISTICS_update (GSF_stats,
760 "# replies dropped due to insufficient cover traffic",
761 1, GNUNET_NO);
762 return;
763 }
764 GSF_cover_content_count -= (reply_anonymity_level - 1);
765 }
766
767 env = GNUNET_MQ_msg_extra (pm,
768 data_len,
769 GNUNET_MESSAGE_TYPE_FS_PUT);
770 pm->type = htonl (type);
771 pm->expiration = GNUNET_TIME_absolute_hton (expiration);
772 GNUNET_memcpy (&pm[1],
773 data,
774 data_len);
775 if ((UINT32_MAX != reply_anonymity_level) &&
776 (0 != reply_anonymity_level) &&
777 (GNUNET_YES == GSF_enable_randomized_delays))
778 {
779 struct GSF_DelayedHandle *dh;
780
781 dh = GNUNET_new (struct GSF_DelayedHandle);
782 dh->cp = cp;
783 dh->env = env;
784 dh->msize = msize;
785 GNUNET_CONTAINER_DLL_insert (cp->delayed_head,
786 cp->delayed_tail,
787 dh);
788 cp->delay_queue_size++;
789 dh->delay_task =
790 GNUNET_SCHEDULER_add_delayed (get_randomized_delay (),
791 &transmit_delayed_now,
792 dh);
793 }
794 else
795 {
796 GSF_peer_transmit_ (cp,
797 GNUNET_NO,
798 UINT32_MAX,
799 env);
800 }
801 if (GNUNET_BLOCK_REPLY_OK_LAST != eval)
802 return;
803 if (NULL == peerreq->kill_task)
804 {
805 GNUNET_STATISTICS_update (GSF_stats,
806 "# P2P searches destroyed due to ultimate reply",
807 1,
808 GNUNET_NO);
809 peerreq->kill_task =
810 GNUNET_SCHEDULER_add_now (&peer_request_destroy,
811 peerreq);
812 }
813}
814
815
816/**
817 * Increase the peer's respect by a value.
818 *
819 * @param cp which peer to change the respect value on
820 * @param value is the int value by which the
821 * peer's credit is to be increased or decreased
822 * @returns the actual change in respect (positive or negative)
823 */
824static int
825change_peer_respect (struct GSF_ConnectedPeer *cp, int value)
826{
827 if (0 == value)
828 return 0;
829 GNUNET_assert (NULL != cp);
830 if (value > 0)
831 {
832 if (cp->ppd.respect + value < cp->ppd.respect)
833 {
834 value = UINT32_MAX - cp->ppd.respect;
835 cp->ppd.respect = UINT32_MAX;
836 }
837 else
838 cp->ppd.respect += value;
839 }
840 else
841 {
842 if (cp->ppd.respect < -value)
843 {
844 value = -cp->ppd.respect;
845 cp->ppd.respect = 0;
846 }
847 else
848 cp->ppd.respect += value;
849 }
850 return value;
851}
852
853
854/**
855 * We've received a request with the specified priority. Bound it
856 * according to how much we respect the given peer.
857 *
858 * @param prio_in requested priority
859 * @param cp the peer making the request
860 * @return effective priority
861 */
862static int32_t
863bound_priority (uint32_t prio_in,
864 struct GSF_ConnectedPeer *cp)
865{
866#define N ((double) 128.0)
867 uint32_t ret;
868 double rret;
869 int ld;
870
871 ld = GSF_test_get_load_too_high_ (0);
872 if (GNUNET_SYSERR == ld)
873 {
874#if INSANE_STATISTICS
875 GNUNET_STATISTICS_update (GSF_stats,
876 gettext_noop
877 ("# requests done for free (low load)"), 1,
878 GNUNET_NO);
879#endif
880 return 0; /* excess resources */
881 }
882 if (prio_in > INT32_MAX)
883 prio_in = INT32_MAX;
884 ret = -change_peer_respect (cp, -(int) prio_in);
885 if (ret > 0)
886 {
887 if (ret > GSF_current_priorities + N)
888 rret = GSF_current_priorities + N;
889 else
890 rret = ret;
891 GSF_current_priorities = (GSF_current_priorities * (N - 1) + rret) / N;
892 }
893 if ((GNUNET_YES == ld) && (ret > 0))
894 {
895 /* try with charging */
896 ld = GSF_test_get_load_too_high_ (ret);
897 }
898 if (GNUNET_YES == ld)
899 {
900 GNUNET_STATISTICS_update (GSF_stats,
901 gettext_noop
902 ("# request dropped, priority insufficient"), 1,
903 GNUNET_NO);
904 /* undo charge */
905 change_peer_respect (cp, (int) ret);
906 return -1; /* not enough resources */
907 }
908 else
909 {
910 GNUNET_STATISTICS_update (GSF_stats,
911 gettext_noop
912 ("# requests done for a price (normal load)"),
913 1,
914 GNUNET_NO);
915 }
916#undef N
917 return ret;
918}
919
920
921/**
922 * The priority level imposes a bound on the maximum
923 * value for the ttl that can be requested.
924 *
925 * @param ttl_in requested ttl
926 * @param prio given priority
927 * @return @a ttl_in if @a ttl_in is below the limit,
928 * otherwise the ttl-limit for the given @a prio
929 */
930static int32_t
931bound_ttl (int32_t ttl_in,
932 uint32_t prio)
933{
934 unsigned long long allowed;
935
936 if (ttl_in <= 0)
937 return ttl_in;
938 allowed = ((unsigned long long) prio) * TTL_DECREMENT / 1000;
939 if (ttl_in > allowed)
940 {
941 if (allowed >= (1 << 30))
942 return 1 << 30;
943 return allowed;
944 }
945 return ttl_in;
946}
947
948
949/**
950 * Closure for #test_exist_cb().
951 */
952struct TestExistClosure
953{
954 /**
955 * Priority of the incoming request.
956 */
957 int32_t priority;
958
959 /**
960 * Relative TTL of the incoming request.
961 */
962 int32_t ttl;
963
964 /**
965 * Type of the incoming request.
966 */
967 enum GNUNET_BLOCK_Type type;
968
969 /**
970 * Set to #GNUNET_YES if we are done handling the query.
971 */
972 int finished;
973};
974
975
976/**
977 * Test if the query already exists. If so, merge it, otherwise
978 * keep `finished` at #GNUNET_NO.
979 *
980 * @param cls our `struct TestExistClosure`
981 * @param hc the key of the query
982 * @param value the existing `struct PeerRequest`.
983 * @return #GNUNET_YES to continue to iterate,
984 * #GNUNET_NO if we successfully merged
985 */
986static int
987test_exist_cb (void *cls,
988 const struct GNUNET_HashCode *hc,
989 void *value)
990{
991 struct TestExistClosure *tec = cls;
992 struct PeerRequest *peerreq = value;
993 struct GSF_PendingRequest *pr;
994 struct GSF_PendingRequestData *prd;
995
996 pr = peerreq->pr;
997 prd = GSF_pending_request_get_data_ (pr);
998 if (prd->type != tec->type)
999 return GNUNET_YES;
1000 if (prd->ttl.abs_value_us >=
1001 GNUNET_TIME_absolute_get ().abs_value_us + tec->ttl * 1000LL)
1002 {
1003 /* existing request has higher TTL, drop new one! */
1004 prd->priority += tec->priority;
1005 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1006 "Have existing request with higher TTL, dropping new request.\n");
1007 GNUNET_STATISTICS_update (GSF_stats,
1008 gettext_noop
1009 ("# requests dropped due to higher-TTL request"),
1010 1, GNUNET_NO);
1011 tec->finished = GNUNET_YES;
1012 return GNUNET_NO;
1013 }
1014 /* existing request has lower TTL, drop old one! */
1015 tec->priority += prd->priority;
1016 free_pending_request (peerreq);
1017 GSF_pending_request_cancel_ (pr,
1018 GNUNET_YES);
1019 return GNUNET_NO;
1020}
1021
1022
1023/**
1024 * Handle P2P "QUERY" message. Creates the pending request entry
1025 * and sets up all of the data structures to that we will
1026 * process replies properly. Does not initiate forwarding or
1027 * local database lookups.
1028 *
1029 * @param cls the other peer involved (sender of the message)
1030 * @param gm the GET message
1031 */
1032void
1033handle_p2p_get (void *cls,
1034 const struct GetMessage *gm)
1035{
1036 struct GSF_ConnectedPeer *cps = cls;
1037 struct PeerRequest *peerreq;
1038 struct GSF_PendingRequest *pr;
1039 struct GSF_ConnectedPeer *cp;
1040 const struct GNUNET_PeerIdentity *target;
1041 enum GSF_PendingRequestOptions options;
1042 uint16_t msize;
1043 unsigned int bits;
1044 const struct GNUNET_PeerIdentity *opt;
1045 uint32_t bm;
1046 size_t bfsize;
1047 uint32_t ttl_decrement;
1048 struct TestExistClosure tec;
1049 GNUNET_PEER_Id spid;
1050 const struct GSF_PendingRequestData *prd;
1051
1052 msize = ntohs (gm->header.size);
1053 tec.type = ntohl (gm->type);
1054 bm = ntohl (gm->hash_bitmap);
1055 bits = 0;
1056 while (bm > 0)
1057 {
1058 if (1 == (bm & 1))
1059 bits++;
1060 bm >>= 1;
1061 }
1062 opt = (const struct GNUNET_PeerIdentity *) &gm[1];
1063 bfsize = msize - sizeof(struct GetMessage) - bits * sizeof(struct
1064 GNUNET_PeerIdentity);
1065 GNUNET_STATISTICS_update (GSF_stats,
1066 gettext_noop
1067 ("# GET requests received (from other peers)"),
1068 1,
1069 GNUNET_NO);
1070 GSF_cover_query_count++;
1071 bm = ntohl (gm->hash_bitmap);
1072 bits = 0;
1073 if (0 != (bm & GET_MESSAGE_BIT_RETURN_TO))
1074 cp = GSF_peer_get_ (&opt[bits++]);
1075 else
1076 cp = cps;
1077 if (NULL == cp)
1078 {
1079 if (0 != (bm & GET_MESSAGE_BIT_RETURN_TO))
1080 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1081 "Failed to find RETURN-TO peer `%s' in connection set. Dropping query.\n",
1082 GNUNET_i2s (&opt[bits - 1]));
1083
1084 else
1085 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1086 "Failed to find peer `%s' in connection set. Dropping query.\n",
1087 GNUNET_i2s (cps->ppd.peer));
1088 GNUNET_STATISTICS_update (GSF_stats,
1089 gettext_noop
1090 (
1091 "# requests dropped due to missing reverse route"),
1092 1,
1093 GNUNET_NO);
1094 return;
1095 }
1096 unsigned int queue_size = GNUNET_MQ_get_length (cp->mq);
1097 queue_size += cp->ppd.pending_replies + cp->delay_queue_size;
1098 if (queue_size > MAX_QUEUE_PER_PEER)
1099 {
1100 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1101 "Peer `%s' has too many replies queued already. Dropping query.\n",
1102 GNUNET_i2s (cps->ppd.peer));
1103 GNUNET_STATISTICS_update (GSF_stats,
1104 gettext_noop (
1105 "# requests dropped due to full reply queue"),
1106 1,
1107 GNUNET_NO);
1108 return;
1109 }
1110 /* note that we can really only check load here since otherwise
1111 * peers could find out that we are overloaded by not being
1112 * disconnected after sending us a malformed query... */
1113 tec.priority = bound_priority (ntohl (gm->priority),
1114 cps);
1115 if (tec.priority < 0)
1116 {
1117 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1118 "Dropping query from `%s', this peer is too busy.\n",
1119 GNUNET_i2s (cps->ppd.peer));
1120 return;
1121 }
1122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1123 "Received request for `%s' of type %u from peer `%s' with flags %u\n",
1124 GNUNET_h2s (&gm->query),
1125 (unsigned int) tec.type,
1126 GNUNET_i2s (cps->ppd.peer),
1127 (unsigned int) bm);
1128 target =
1129 (0 !=
1130 (bm & GET_MESSAGE_BIT_TRANSMIT_TO)) ? (&opt[bits++]) : NULL;
1131 options = GSF_PRO_DEFAULTS;
1132 spid = 0;
1133 if ((GNUNET_LOAD_get_load (cp->ppd.transmission_delay) > 3 * (1
1134 + tec.priority))
1135 || (GNUNET_LOAD_get_average (cp->ppd.transmission_delay) >
1136 GNUNET_CONSTANTS_MAX_CORK_DELAY.rel_value_us * 2
1137 + GNUNET_LOAD_get_average (GSF_rt_entry_lifetime)))
1138 {
1139 /* don't have BW to send to peer, or would likely take longer than we have for it,
1140 * so at best indirect the query */
1141 tec.priority = 0;
1142 options |= GSF_PRO_FORWARD_ONLY;
1143 spid = GNUNET_PEER_intern (cps->ppd.peer);
1144 GNUNET_assert (0 != spid);
1145 }
1146 tec.ttl = bound_ttl (ntohl (gm->ttl),
1147 tec.priority);
1148 /* decrement ttl (always) */
1149 ttl_decrement =
1150 2 * TTL_DECREMENT + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1151 TTL_DECREMENT);
1152 if ((tec.ttl < 0) &&
1153 (((int32_t) (tec.ttl - ttl_decrement)) > 0))
1154 {
1155 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1156 "Dropping query from `%s' due to TTL underflow (%d - %u).\n",
1157 GNUNET_i2s (cps->ppd.peer),
1158 tec.ttl,
1159 ttl_decrement);
1160 GNUNET_STATISTICS_update (GSF_stats,
1161 gettext_noop
1162 ("# requests dropped due TTL underflow"), 1,
1163 GNUNET_NO);
1164 /* integer underflow => drop (should be very rare)! */
1165 return;
1166 }
1167 tec.ttl -= ttl_decrement;
1168
1169 /* test if the request already exists */
1170 tec.finished = GNUNET_NO;
1171 GNUNET_CONTAINER_multihashmap_get_multiple (cp->request_map,
1172 &gm->query,
1173 &test_exist_cb,
1174 &tec);
1175 if (GNUNET_YES == tec.finished)
1176 return; /* merged into existing request, we're done */
1177
1178 peerreq = GNUNET_new (struct PeerRequest);
1179 peerreq->cp = cp;
1180 pr = GSF_pending_request_create_ (options,
1181 tec.type,
1182 &gm->query,
1183 target,
1184 (bfsize > 0)
1185 ? (const char *) &opt[bits]
1186 : NULL,
1187 bfsize,
1188 1 /* anonymity */,
1189 (uint32_t) tec.priority,
1190 tec.ttl,
1191 spid,
1192 GNUNET_PEER_intern (cps->ppd.peer),
1193 NULL, 0, /* replies_seen */
1194 &handle_p2p_reply,
1195 peerreq);
1196 GNUNET_assert (NULL != pr);
1197 prd = GSF_pending_request_get_data_ (pr);
1198 peerreq->pr = pr;
1199 GNUNET_break (GNUNET_OK ==
1200 GNUNET_CONTAINER_multihashmap_put (cp->request_map,
1201 &prd->query,
1202 peerreq,
1203 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
1204 GNUNET_STATISTICS_update (GSF_stats,
1205 gettext_noop (
1206 "# P2P query messages received and processed"),
1207 1,
1208 GNUNET_NO);
1209 GNUNET_STATISTICS_update (GSF_stats,
1210 gettext_noop ("# P2P searches active"),
1211 1,
1212 GNUNET_NO);
1213 GSF_pending_request_get_data_ (pr)->has_started = GNUNET_YES;
1214 GSF_local_lookup_ (pr,
1215 &GSF_consider_forwarding,
1216 NULL);
1217}
1218
1219
1220/**
1221 * Transmit a message to the given peer as soon as possible.
1222 * If the peer disconnects before the transmission can happen,
1223 * the callback is invoked with a `NULL` @a buffer.
1224 *
1225 * @param cp target peer
1226 * @param is_query is this a query (#GNUNET_YES) or content (#GNUNET_NO) or neither (#GNUNET_SYSERR)
1227 * @param priority how important is this request?
1228 * @param env message to send
1229 */
1230void
1231GSF_peer_transmit_ (struct GSF_ConnectedPeer *cp,
1232 int is_query,
1233 uint32_t priority,
1234 struct GNUNET_MQ_Envelope *env)
1235{
1236 struct GSF_PeerTransmitHandle *pth;
1237 struct GSF_PeerTransmitHandle *pos;
1238 struct GSF_PeerTransmitHandle *prev;
1239
1240 pth = GNUNET_new (struct GSF_PeerTransmitHandle);
1241 pth->transmission_request_start_time = GNUNET_TIME_absolute_get ();
1242 pth->env = env;
1243 pth->is_query = is_query;
1244 pth->priority = priority;
1245 pth->cp = cp;
1246 /* insertion sort (by priority, descending) */
1247 prev = NULL;
1248 pos = cp->pth_head;
1249 while ((NULL != pos) && (pos->priority > priority))
1250 {
1251 prev = pos;
1252 pos = pos->next;
1253 }
1254 GNUNET_CONTAINER_DLL_insert_after (cp->pth_head,
1255 cp->pth_tail,
1256 prev,
1257 pth);
1258 if (GNUNET_YES == is_query)
1259 cp->ppd.pending_queries++;
1260 else if (GNUNET_NO == is_query)
1261 cp->ppd.pending_replies++;
1262 schedule_transmission (pth);
1263}
1264
1265
1266/**
1267 * Report on receiving a reply; update the performance record of the given peer.
1268 *
1269 * @param cp responding peer (will be updated)
1270 * @param request_time time at which the original query was transmitted
1271 * @param request_priority priority of the original request
1272 */
1273void
1274GSF_peer_update_performance_ (struct GSF_ConnectedPeer *cp,
1275 struct GNUNET_TIME_Absolute request_time,
1276 uint32_t request_priority)
1277{
1278 struct GNUNET_TIME_Relative delay;
1279
1280 delay = GNUNET_TIME_absolute_get_duration (request_time);
1281 cp->ppd.avg_reply_delay.rel_value_us =
1282 (cp->ppd.avg_reply_delay.rel_value_us * (RUNAVG_DELAY_N - 1)
1283 + delay.rel_value_us) / RUNAVG_DELAY_N;
1284 cp->ppd.avg_priority =
1285 (cp->ppd.avg_priority * (RUNAVG_DELAY_N - 1)
1286 + request_priority) / RUNAVG_DELAY_N;
1287}
1288
1289
1290/**
1291 * Report on receiving a reply in response to an initiating client.
1292 * Remember that this peer is good for this client.
1293 *
1294 * @param cp responding peer (will be updated)
1295 * @param initiator_client local client on responsible for query
1296 */
1297void
1298GSF_peer_update_responder_client_ (struct GSF_ConnectedPeer *cp,
1299 struct GSF_LocalClient *initiator_client)
1300{
1301 cp->ppd.last_client_replies[cp->last_client_replies_woff++
1302 % CS2P_SUCCESS_LIST_SIZE] = initiator_client;
1303}
1304
1305
1306/**
1307 * Report on receiving a reply in response to an initiating peer.
1308 * Remember that this peer is good for this initiating peer.
1309 *
1310 * @param cp responding peer (will be updated)
1311 * @param initiator_peer other peer responsible for query
1312 */
1313void
1314GSF_peer_update_responder_peer_ (struct GSF_ConnectedPeer *cp,
1315 const struct GSF_ConnectedPeer *initiator_peer)
1316{
1317 unsigned int woff;
1318
1319 woff = cp->last_p2p_replies_woff % P2P_SUCCESS_LIST_SIZE;
1320 GNUNET_PEER_change_rc (cp->ppd.last_p2p_replies[woff], -1);
1321 cp->ppd.last_p2p_replies[woff] = initiator_peer->ppd.pid;
1322 GNUNET_PEER_change_rc (initiator_peer->ppd.pid, 1);
1323 cp->last_p2p_replies_woff = (woff + 1) % P2P_SUCCESS_LIST_SIZE;
1324}
1325
1326
1327/**
1328 * Write peer-respect information to a file - flush the buffer entry!
1329 *
1330 * @param cls unused
1331 * @param key peer identity
1332 * @param value the `struct GSF_ConnectedPeer` to flush
1333 * @return #GNUNET_OK to continue iteration
1334 */
1335static int
1336flush_respect (void *cls,
1337 const struct GNUNET_PeerIdentity *key,
1338 void *value)
1339{
1340 struct GSF_ConnectedPeer *cp = value;
1341 struct GNUNET_PeerIdentity pid;
1342
1343 if (cp->ppd.respect == cp->disk_respect)
1344 return GNUNET_OK; /* unchanged */
1345 GNUNET_assert (0 != cp->ppd.pid);
1346 GNUNET_PEER_resolve (cp->ppd.pid, &pid);
1347 GNUNET_PEERSTORE_store (peerstore, "fs", &pid, "respect", &cp->ppd.respect,
1348 sizeof(cp->ppd.respect),
1349 GNUNET_TIME_UNIT_FOREVER_ABS,
1350 GNUNET_PEERSTORE_STOREOPTION_REPLACE,
1351 NULL,
1352 NULL);
1353 return GNUNET_OK;
1354}
1355
1356
1357void
1358GSF_peer_disconnect_handler (void *cls,
1359 const struct GNUNET_PeerIdentity *peer,
1360 void *internal_cls)
1361{
1362 struct GSF_ConnectedPeer *cp = internal_cls;
1363 struct GSF_PeerTransmitHandle *pth;
1364 struct GSF_DelayedHandle *dh;
1365
1366 if (NULL == cp)
1367 return; /* must have been disconnect from core with
1368 * 'peer' == my_id, ignore */
1369 flush_respect (NULL,
1370 peer,
1371 cp);
1372 GNUNET_assert (GNUNET_YES ==
1373 GNUNET_CONTAINER_multipeermap_remove (cp_map,
1374 peer,
1375 cp));
1376 GNUNET_STATISTICS_set (GSF_stats,
1377 gettext_noop ("# peers connected"),
1378 GNUNET_CONTAINER_multipeermap_size (cp_map),
1379 GNUNET_NO);
1380 if (NULL != cp->respect_iterate_req)
1381 {
1382 GNUNET_PEERSTORE_iteration_stop (cp->respect_iterate_req);
1383 cp->respect_iterate_req = NULL;
1384 }
1385 GNUNET_CONTAINER_multihashmap_iterate (cp->request_map,
1386 &cancel_pending_request,
1387 cp);
1388 GNUNET_CONTAINER_multihashmap_destroy (cp->request_map);
1389 cp->request_map = NULL;
1390 GSF_plan_notify_peer_disconnect_ (cp);
1391 GNUNET_LOAD_value_free (cp->ppd.transmission_delay);
1392 GNUNET_PEER_decrement_rcs (cp->ppd.last_p2p_replies,
1393 P2P_SUCCESS_LIST_SIZE);
1394 memset (cp->ppd.last_p2p_replies,
1395 0,
1396 sizeof(cp->ppd.last_p2p_replies));
1397 GSF_push_stop_ (cp);
1398 while (NULL != (pth = cp->pth_head))
1399 {
1400 GNUNET_CONTAINER_DLL_remove (cp->pth_head,
1401 cp->pth_tail,
1402 pth);
1403 if (GNUNET_YES == pth->is_query)
1404 GNUNET_assert (0 < cp->ppd.pending_queries--);
1405 else if (GNUNET_NO == pth->is_query)
1406 GNUNET_assert (0 < cp->ppd.pending_replies--);
1407 GNUNET_free (pth);
1408 }
1409 while (NULL != (dh = cp->delayed_head))
1410 {
1411 GNUNET_CONTAINER_DLL_remove (cp->delayed_head,
1412 cp->delayed_tail,
1413 dh);
1414 GNUNET_MQ_discard (dh->env);
1415 cp->delay_queue_size--;
1416 GNUNET_SCHEDULER_cancel (dh->delay_task);
1417 GNUNET_free (dh);
1418 }
1419 GNUNET_PEER_change_rc (cp->ppd.pid, -1);
1420 if (NULL != cp->mig_revive_task)
1421 {
1422 GNUNET_SCHEDULER_cancel (cp->mig_revive_task);
1423 cp->mig_revive_task = NULL;
1424 }
1425 GNUNET_break (0 == cp->ppd.pending_queries);
1426 GNUNET_break (0 == cp->ppd.pending_replies);
1427 GNUNET_free (cp);
1428}
1429
1430
1431/**
1432 * Closure for #call_iterator().
1433 */
1434struct IterationContext
1435{
1436 /**
1437 * Function to call on each entry.
1438 */
1439 GSF_ConnectedPeerIterator it;
1440
1441 /**
1442 * Closure for @e it.
1443 */
1444 void *it_cls;
1445};
1446
1447
1448/**
1449 * Function that calls the callback for each peer.
1450 *
1451 * @param cls the `struct IterationContext *`
1452 * @param key identity of the peer
1453 * @param value the `struct GSF_ConnectedPeer *`
1454 * @return #GNUNET_YES to continue iteration
1455 */
1456static int
1457call_iterator (void *cls,
1458 const struct GNUNET_PeerIdentity *key,
1459 void *value)
1460{
1461 struct IterationContext *ic = cls;
1462 struct GSF_ConnectedPeer *cp = value;
1463
1464 ic->it (ic->it_cls,
1465 key, cp,
1466 &cp->ppd);
1467 return GNUNET_YES;
1468}
1469
1470
1471void
1472GSF_iterate_connected_peers_ (GSF_ConnectedPeerIterator it,
1473 void *it_cls)
1474{
1475 struct IterationContext ic;
1476
1477 ic.it = it;
1478 ic.it_cls = it_cls;
1479 GNUNET_CONTAINER_multipeermap_iterate (cp_map,
1480 &call_iterator,
1481 &ic);
1482}
1483
1484
1485/**
1486 * Obtain the identity of a connected peer.
1487 *
1488 * @param cp peer to get identity of
1489 * @param id identity to set (written to)
1490 */
1491void
1492GSF_connected_peer_get_identity_ (const struct GSF_ConnectedPeer *cp,
1493 struct GNUNET_PeerIdentity *id)
1494{
1495 GNUNET_assert (0 != cp->ppd.pid);
1496 GNUNET_PEER_resolve (cp->ppd.pid, id);
1497}
1498
1499
1500/**
1501 * Obtain the identity of a connected peer.
1502 *
1503 * @param cp peer to get identity of
1504 * @return reference to peer identity, valid until peer disconnects (!)
1505 */
1506const struct GNUNET_PeerIdentity *
1507GSF_connected_peer_get_identity2_ (const struct GSF_ConnectedPeer *cp)
1508{
1509 GNUNET_assert (0 != cp->ppd.pid);
1510 return GNUNET_PEER_resolve2 (cp->ppd.pid);
1511}
1512
1513
1514/**
1515 * Ask a peer to stop migrating data to us until the given point
1516 * in time.
1517 *
1518 * @param cp peer to ask
1519 * @param block_time until when to block
1520 */
1521void
1522GSF_block_peer_migration_ (struct GSF_ConnectedPeer *cp,
1523 struct GNUNET_TIME_Absolute block_time)
1524{
1525 struct GNUNET_MQ_Envelope *env;
1526 struct MigrationStopMessage *msm;
1527
1528 if (cp->last_migration_block.abs_value_us > block_time.abs_value_us)
1529 {
1530 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1531 "Migration already blocked for another %s\n",
1532 GNUNET_STRINGS_relative_time_to_string (
1533 GNUNET_TIME_absolute_get_remaining
1534 (cp->
1535 last_migration_block), GNUNET_YES));
1536 return; /* already blocked */
1537 }
1538 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asking to stop migration for %s\n",
1539 GNUNET_STRINGS_relative_time_to_string (
1540 GNUNET_TIME_absolute_get_remaining (block_time),
1541 GNUNET_YES));
1542 cp->last_migration_block = block_time;
1543 env = GNUNET_MQ_msg (msm,
1544 GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP);
1545 msm->reserved = htonl (0);
1546 msm->duration
1547 = GNUNET_TIME_relative_hton (GNUNET_TIME_absolute_get_remaining
1548 (cp->last_migration_block));
1549 GNUNET_STATISTICS_update (GSF_stats,
1550 gettext_noop ("# migration stop messages sent"),
1551 1,
1552 GNUNET_NO);
1553 GSF_peer_transmit_ (cp,
1554 GNUNET_SYSERR,
1555 UINT32_MAX,
1556 env);
1557}
1558
1559
1560/**
1561 * Notify core about a preference we have for the given peer
1562 * (to allocate more resources towards it). The change will
1563 * be communicated the next time we reserve bandwidth with
1564 * core (not instantly).
1565 *
1566 * @param cp peer to reserve bandwidth from
1567 * @param pref preference change
1568 */
1569void
1570GSF_connected_peer_change_preference_ (struct GSF_ConnectedPeer *cp,
1571 uint64_t pref)
1572{
1573 cp->inc_preference += pref;
1574}
1575
1576
1577/**
1578 * Call this method periodically to flush respect information to disk.
1579 *
1580 * @param cls closure, not used
1581 */
1582static void
1583cron_flush_respect (void *cls)
1584{
1585 fr_task = NULL;
1586 GNUNET_CONTAINER_multipeermap_iterate (cp_map,
1587 &flush_respect,
1588 NULL);
1589 fr_task = GNUNET_SCHEDULER_add_delayed_with_priority (RESPECT_FLUSH_FREQ,
1590 GNUNET_SCHEDULER_PRIORITY_HIGH,
1591 &cron_flush_respect,
1592 NULL);
1593}
1594
1595
1596/**
1597 * Initialize peer management subsystem.
1598 */
1599void
1600GSF_connected_peer_init_ ()
1601{
1602 cp_map = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES);
1603 peerstore = GNUNET_PEERSTORE_connect (GSF_cfg);
1604 fr_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_HIGH,
1605 &cron_flush_respect, NULL);
1606}
1607
1608
1609/**
1610 * Shutdown peer management subsystem.
1611 */
1612void
1613GSF_connected_peer_done_ ()
1614{
1615 GNUNET_CONTAINER_multipeermap_iterate (cp_map,
1616 &flush_respect,
1617 NULL);
1618 GNUNET_SCHEDULER_cancel (fr_task);
1619 fr_task = NULL;
1620 GNUNET_CONTAINER_multipeermap_destroy (cp_map);
1621 cp_map = NULL;
1622 GNUNET_PEERSTORE_disconnect (peerstore);
1623}
1624
1625
1626/**
1627 * Iterator to remove references to LC entry.
1628 *
1629 * @param cls the `struct GSF_LocalClient *` to look for
1630 * @param key current key code
1631 * @param value value in the hash map (peer entry)
1632 * @return #GNUNET_YES (we should continue to iterate)
1633 */
1634static int
1635clean_local_client (void *cls,
1636 const struct GNUNET_PeerIdentity *key,
1637 void *value)
1638{
1639 const struct GSF_LocalClient *lc = cls;
1640 struct GSF_ConnectedPeer *cp = value;
1641 unsigned int i;
1642
1643 for (i = 0; i < CS2P_SUCCESS_LIST_SIZE; i++)
1644 if (cp->ppd.last_client_replies[i] == lc)
1645 cp->ppd.last_client_replies[i] = NULL;
1646 return GNUNET_YES;
1647}
1648
1649
1650/**
1651 * Notification that a local client disconnected. Clean up all of our
1652 * references to the given handle.
1653 *
1654 * @param lc handle to the local client (henceforth invalid)
1655 */
1656void
1657GSF_handle_local_client_disconnect_ (const struct GSF_LocalClient *lc)
1658{
1659 if (NULL == cp_map)
1660 return; /* already cleaned up */
1661 GNUNET_CONTAINER_multipeermap_iterate (cp_map,
1662 &clean_local_client,
1663 (void *) lc);
1664}
1665
1666
1667/* end of gnunet-service-fs_cp.c */
diff --git a/src/service/fs/gnunet-service-fs_cp.h b/src/service/fs/gnunet-service-fs_cp.h
new file mode 100644
index 000000000..cea82b10c
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_cp.h
@@ -0,0 +1,415 @@
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/service/fs/gnunet-service-fs_indexing.c b/src/service/fs/gnunet-service-fs_indexing.c
new file mode 100644
index 000000000..8fb34c067
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_indexing.c
@@ -0,0 +1,495 @@
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/service/fs/gnunet-service-fs_indexing.h b/src/service/fs/gnunet-service-fs_indexing.h
new file mode 100644
index 000000000..244a51900
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_indexing.h
@@ -0,0 +1,120 @@
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/service/fs/gnunet-service-fs_pe.c b/src/service/fs/gnunet-service-fs_pe.c
new file mode 100644
index 000000000..60dd0ab70
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_pe.c
@@ -0,0 +1,814 @@
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/service/fs/gnunet-service-fs_pe.h b/src/service/fs/gnunet-service-fs_pe.h
new file mode 100644
index 000000000..d532b6b71
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_pe.h
@@ -0,0 +1,95 @@
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/service/fs/gnunet-service-fs_pr.c b/src/service/fs/gnunet-service-fs_pr.c
new file mode 100644
index 000000000..f192c017d
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_pr.c
@@ -0,0 +1,1887 @@
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/service/fs/gnunet-service-fs_pr.h b/src/service/fs/gnunet-service-fs_pr.h
new file mode 100644
index 000000000..339e409c5
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_pr.h
@@ -0,0 +1,422 @@
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/service/fs/gnunet-service-fs_push.c b/src/service/fs/gnunet-service-fs_push.c
new file mode 100644
index 000000000..92dbba8e6
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_push.c
@@ -0,0 +1,672 @@
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/service/fs/gnunet-service-fs_push.h b/src/service/fs/gnunet-service-fs_push.h
new file mode 100644
index 000000000..2cd621bbb
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_push.h
@@ -0,0 +1,66 @@
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/service/fs/gnunet-service-fs_put.c b/src/service/fs/gnunet-service-fs_put.c
new file mode 100644
index 000000000..ca2c85724
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_put.c
@@ -0,0 +1,290 @@
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/service/fs/gnunet-service-fs_put.h b/src/service/fs/gnunet-service-fs_put.h
new file mode 100644
index 000000000..b6c9ba86f
--- /dev/null
+++ b/src/service/fs/gnunet-service-fs_put.h
@@ -0,0 +1,46 @@
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/service/fs/meson.build b/src/service/fs/meson.build
new file mode 100644
index 000000000..9e3e05cf7
--- /dev/null
+++ b/src/service/fs/meson.build
@@ -0,0 +1,82 @@
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
41endif
42
43libgnunetfs = library('gnunetfs',
44 libgnunetfs_src,
45 soversion: '2',
46 version: '2.1.1',
47 dependencies: [libgnunetutil_dep,
48 libgnunetdatastore_dep,
49 libgnunetstatistics_dep,
50 unistr_dep],
51 include_directories: [incdir, configuration_inc],
52 install: true,
53 install_dir: get_option('libdir'))
54libgnunetfs_dep = declare_dependency(link_with : libgnunetfs)
55pkg.generate(libgnunetfs, url: 'https://www.gnunet.org',
56 description : 'Provides API for GNUnet File-Sharing service')
57
58executable ('gnunet-service-fs',
59 gnunetservicefs_src,
60 dependencies: [libgnunetfs_dep,
61 libgnunetutil_dep,
62 libgnunetstatistics_dep,
63 libgnunetcore_dep,
64 libgnunetdht_dep,
65 libgnunetidentity_dep,
66 m_dep,
67 libgnunetcadet_dep,
68 libgnunetpeerstore_dep,
69 libgnunetdatastore_dep,
70 libgnunetblock_dep],
71 include_directories: [incdir, configuration_inc],
72 install: true,
73 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
74executable ('gnunet-helper-fs-publish',
75 ['gnunet-helper-fs-publish.c'],
76 dependencies: [libgnunetfs_dep,
77 libgnunetutil_dep,
78 libgnunetblock_dep],
79 include_directories: [incdir, configuration_inc],
80 install: true,
81 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
82
diff --git a/src/service/fs/meta_data.c b/src/service/fs/meta_data.c
new file mode 100644
index 000000000..b3db0e6c7
--- /dev/null
+++ b/src/service/fs/meta_data.c
@@ -0,0 +1,1238 @@
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 * Meta data item.
44 */
45struct MetaItem
46{
47 /**
48 * This is a doubly linked list.
49 */
50 struct MetaItem *next;
51
52 /**
53 * This is a doubly linked list.
54 */
55 struct MetaItem *prev;
56
57 /**
58 * Name of the extracting plugin.
59 */
60 char *plugin_name;
61
62 /**
63 * Mime-type of data.
64 */
65 char *mime_type;
66
67 /**
68 * The actual meta data.
69 */
70 char *data;
71
72 /**
73 * Number of bytes in 'data'.
74 */
75 size_t data_size;
76
77 /**
78 * Type of the meta data.
79 */
80 enum EXTRACTOR_MetaType type;
81
82 /**
83 * Format of the meta data.
84 */
85 enum EXTRACTOR_MetaFormat format;
86};
87
88/**
89 * Meta data to associate with a file, directory or namespace.
90 */
91struct GNUNET_FS_MetaData
92{
93 /**
94 * Head of linked list of the meta data items.
95 */
96 struct MetaItem *items_head;
97
98 /**
99 * Tail of linked list of the meta data items.
100 */
101 struct MetaItem *items_tail;
102
103 /**
104 * Complete serialized and compressed buffer of the items.
105 * NULL if we have not computed that buffer yet.
106 */
107 char *sbuf;
108
109 /**
110 * Number of bytes in 'sbuf'. 0 if the buffer is stale.
111 */
112 size_t sbuf_size;
113
114 /**
115 * Number of items in the linked list.
116 */
117 unsigned int item_count;
118};
119
120
121/**
122 * Create a fresh struct FS_MetaData token.
123 *
124 * @return empty meta-data container
125 */
126struct GNUNET_FS_MetaData *
127GNUNET_FS_meta_data_create ()
128{
129 return GNUNET_new (struct GNUNET_FS_MetaData);
130}
131
132
133/**
134 * Free meta data item.
135 *
136 * @param mi item to free
137 */
138static void
139meta_item_free (struct MetaItem *mi)
140{
141 GNUNET_free (mi->plugin_name);
142 GNUNET_free (mi->mime_type);
143 GNUNET_free (mi->data);
144 GNUNET_free (mi);
145}
146
147
148/**
149 * The meta data has changed, invalidate its serialization
150 * buffer.
151 *
152 * @param md meta data that changed
153 */
154static void
155invalidate_sbuf (struct GNUNET_FS_MetaData *md)
156{
157 if (NULL == md->sbuf)
158 return;
159 GNUNET_free (md->sbuf);
160 md->sbuf = NULL;
161 md->sbuf_size = 0;
162}
163
164
165void
166GNUNET_FS_meta_data_destroy (struct GNUNET_FS_MetaData *md)
167{
168 struct MetaItem *pos;
169
170 if (NULL == md)
171 return;
172 while (NULL != (pos = md->items_head))
173 {
174 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
175 meta_item_free (pos);
176 }
177 GNUNET_free (md->sbuf);
178 GNUNET_free (md);
179}
180
181
182void
183GNUNET_FS_meta_data_clear (struct GNUNET_FS_MetaData *md)
184{
185 struct MetaItem *mi;
186
187 if (NULL == md)
188 return;
189 while (NULL != (mi = md->items_head))
190 {
191 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, mi);
192 meta_item_free (mi);
193 }
194 GNUNET_free (md->sbuf);
195 memset (md, 0, sizeof(struct GNUNET_FS_MetaData));
196}
197
198
199int
200GNUNET_FS_meta_data_test_equal (const struct GNUNET_FS_MetaData
201 *md1,
202 const struct GNUNET_FS_MetaData
203 *md2)
204{
205 struct MetaItem *i;
206 struct MetaItem *j;
207 int found;
208
209 if (md1 == md2)
210 return GNUNET_YES;
211 if (md1->item_count != md2->item_count)
212 return GNUNET_NO;
213 for (i = md1->items_head; NULL != i; i = i->next)
214 {
215 found = GNUNET_NO;
216 for (j = md2->items_head; NULL != j; j = j->next)
217 {
218 if ((i->type == j->type) && (i->format == j->format) &&
219 (i->data_size == j->data_size) &&
220 (0 == memcmp (i->data, j->data, i->data_size)))
221 {
222 found = GNUNET_YES;
223 break;
224 }
225 if (j->data_size < i->data_size)
226 break; /* elements are sorted by (decreasing) size... */
227 }
228 if (GNUNET_NO == found)
229 return GNUNET_NO;
230 }
231 return GNUNET_YES;
232}
233
234
235/**
236 * Extend metadata. Note that the list of meta data items is
237 * sorted by size (largest first).
238 *
239 * @param md metadata to extend
240 * @param plugin_name name of the plugin that produced this value;
241 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
242 * used in the main libextractor library and yielding
243 * meta data).
244 * @param type libextractor-type describing the meta data
245 * @param format basic format information about data
246 * @param data_mime_type mime-type of data (not of the original file);
247 * can be NULL (if mime-type is not known)
248 * @param data actual meta-data found
249 * @param data_size number of bytes in @a data
250 * @return #GNUNET_OK on success, #GNUNET_SYSERR if this entry already exists
251 * data_mime_type and plugin_name are not considered for "exists" checks
252 */
253int
254GNUNET_FS_meta_data_insert (struct GNUNET_FS_MetaData *md,
255 const char *plugin_name,
256 enum EXTRACTOR_MetaType type,
257 enum EXTRACTOR_MetaFormat format,
258 const char *data_mime_type, const char *data,
259 size_t data_size)
260{
261 struct MetaItem *pos;
262 struct MetaItem *mi;
263 char *p;
264
265 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
266 (EXTRACTOR_METAFORMAT_C_STRING == format))
267 GNUNET_break ('\0' == data[data_size - 1]);
268
269 for (pos = md->items_head; NULL != pos; pos = pos->next)
270 {
271 if (pos->data_size < data_size)
272 break; /* elements are sorted by size in the list */
273 if ((pos->type == type) && (pos->data_size == data_size) &&
274 (0 == memcmp (pos->data, data, data_size)))
275 {
276 if ((NULL == pos->mime_type) && (NULL != data_mime_type))
277 {
278 pos->mime_type = GNUNET_strdup (data_mime_type);
279 invalidate_sbuf (md);
280 }
281 if ((EXTRACTOR_METAFORMAT_C_STRING == pos->format) &&
282 (EXTRACTOR_METAFORMAT_UTF8 == format))
283 {
284 pos->format = EXTRACTOR_METAFORMAT_UTF8;
285 invalidate_sbuf (md);
286 }
287 return GNUNET_SYSERR;
288 }
289 }
290 md->item_count++;
291 mi = GNUNET_new (struct MetaItem);
292 mi->type = type;
293 mi->format = format;
294 mi->data_size = data_size;
295 if (NULL == pos)
296 GNUNET_CONTAINER_DLL_insert_tail (md->items_head,
297 md->items_tail,
298 mi);
299 else
300 GNUNET_CONTAINER_DLL_insert_after (md->items_head,
301 md->items_tail,
302 pos->prev,
303 mi);
304 mi->mime_type =
305 (NULL == data_mime_type) ? NULL : GNUNET_strdup (data_mime_type);
306 mi->plugin_name = (NULL == plugin_name) ? NULL : GNUNET_strdup (plugin_name);
307 mi->data = GNUNET_malloc (data_size);
308 GNUNET_memcpy (mi->data, data, data_size);
309 /* change all dir separators to POSIX style ('/') */
310 if ((EXTRACTOR_METATYPE_FILENAME == type) ||
311 (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type))
312 {
313 p = mi->data;
314 while (('\0' != *p) && (p < mi->data + data_size))
315 {
316 if ('\\' == *p)
317 *p = '/';
318 p++;
319 }
320 }
321 invalidate_sbuf (md);
322 return GNUNET_OK;
323}
324
325
326/**
327 * Merge given meta data.
328 *
329 * @param cls the `struct GNUNET_FS_MetaData` to merge into
330 * @param plugin_name name of the plugin that produced this value;
331 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
332 * used in the main libextractor library and yielding
333 * meta data).
334 * @param type libextractor-type describing the meta data
335 * @param format basic format information about data
336 * @param data_mime_type mime-type of data (not of the original file);
337 * can be NULL (if mime-type is not known)
338 * @param data actual meta-data found
339 * @param data_size number of bytes in @a data
340 * @return 0 (to continue)
341 */
342static int
343merge_helper (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
344 enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
345 const char *data, size_t data_size)
346{
347 struct GNUNET_FS_MetaData *md = cls;
348
349 (void) GNUNET_FS_meta_data_insert (md, plugin_name, type, format,
350 data_mime_type, data, data_size);
351 return 0;
352}
353
354
355void
356GNUNET_FS_meta_data_merge (struct GNUNET_FS_MetaData *md,
357 const struct GNUNET_FS_MetaData *in)
358{
359 GNUNET_FS_meta_data_iterate (in, &merge_helper, md);
360}
361
362
363int
364GNUNET_FS_meta_data_delete (struct GNUNET_FS_MetaData *md,
365 enum EXTRACTOR_MetaType type,
366 const char *data, size_t data_size)
367{
368 struct MetaItem *pos;
369
370 for (pos = md->items_head; NULL != pos; pos = pos->next)
371 {
372 if (pos->data_size < data_size)
373 break; /* items are sorted by (decreasing) size */
374 if ((pos->type == type) &&
375 ((NULL == data) ||
376 ((pos->data_size == data_size) &&
377 (0 == memcmp (pos->data, data, data_size)))))
378 {
379 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
380 meta_item_free (pos);
381 md->item_count--;
382 invalidate_sbuf (md);
383 return GNUNET_OK;
384 }
385 }
386 return GNUNET_SYSERR;
387}
388
389
390void
391GNUNET_FS_meta_data_add_publication_date (struct
392 GNUNET_FS_MetaData *md)
393{
394 const char *dat;
395 struct GNUNET_TIME_Absolute t;
396
397 t = GNUNET_TIME_absolute_get ();
398 GNUNET_FS_meta_data_delete (md,
399 EXTRACTOR_METATYPE_PUBLICATION_DATE,
400 NULL, 0);
401 dat = GNUNET_STRINGS_absolute_time_to_string (t);
402 GNUNET_FS_meta_data_insert (md, "<gnunet>",
403 EXTRACTOR_METATYPE_PUBLICATION_DATE,
404 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
405 dat, strlen (dat) + 1);
406}
407
408
409/**
410 * Iterate over MD entries.
411 *
412 * @param md metadata to inspect
413 * @param iter function to call on each entry
414 * @param iter_cls closure for iterator
415 * @return number of entries
416 */
417int
418GNUNET_FS_meta_data_iterate (const struct GNUNET_FS_MetaData *md,
419 EXTRACTOR_MetaDataProcessor iter,
420 void *iter_cls)
421{
422 struct MetaItem *pos;
423
424 if (NULL == md)
425 return 0;
426 if (NULL == iter)
427 return md->item_count;
428 for (pos = md->items_head; NULL != pos; pos = pos->next)
429 if (0 !=
430 iter (iter_cls, pos->plugin_name, pos->type, pos->format,
431 pos->mime_type, pos->data, pos->data_size))
432 return md->item_count;
433 return md->item_count;
434}
435
436
437char *
438GNUNET_FS_meta_data_get_by_type (const struct
439 GNUNET_FS_MetaData *md,
440 enum EXTRACTOR_MetaType type)
441{
442 struct MetaItem *pos;
443
444 if (NULL == md)
445 return NULL;
446 for (pos = md->items_head; NULL != pos; pos = pos->next)
447 if ((type == pos->type) &&
448 ((pos->format == EXTRACTOR_METAFORMAT_UTF8) ||
449 (pos->format == EXTRACTOR_METAFORMAT_C_STRING)))
450 return GNUNET_strdup (pos->data);
451 return NULL;
452}
453
454
455char *
456GNUNET_FS_meta_data_get_first_by_types (const struct
457 GNUNET_FS_MetaData *md,
458 ...)
459{
460 char *ret;
461 va_list args;
462 int type;
463
464 if (NULL == md)
465 return NULL;
466 ret = NULL;
467 va_start (args, md);
468 while (1)
469 {
470 type = va_arg (args, int);
471 if (-1 == type)
472 break;
473 if (NULL != (ret = GNUNET_FS_meta_data_get_by_type (md, type)))
474 break;
475 }
476 va_end (args);
477 return ret;
478}
479
480
481/**
482 * Get a thumbnail from the meta-data (if present).
483 *
484 * @param md metadata to get the thumbnail from
485 * @param thumb will be set to the thumbnail data. Must be
486 * freed by the caller!
487 * @return number of bytes in thumbnail, 0 if not available
488 */
489size_t
490GNUNET_FS_meta_data_get_thumbnail (const struct GNUNET_FS_MetaData
491 *md, unsigned char **thumb)
492{
493 struct MetaItem *pos;
494 struct MetaItem *match;
495
496 if (NULL == md)
497 return 0;
498 match = NULL;
499 for (pos = md->items_head; NULL != pos; pos = pos->next)
500 {
501 if ((NULL != pos->mime_type) &&
502 (0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) &&
503 (EXTRACTOR_METAFORMAT_BINARY == pos->format))
504 {
505 if (NULL == match)
506 match = pos;
507 else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
508 (pos->type == EXTRACTOR_METATYPE_THUMBNAIL))
509 match = pos;
510 }
511 }
512 if ((NULL == match) || (0 == match->data_size))
513 return 0;
514 *thumb = GNUNET_malloc (match->data_size);
515 GNUNET_memcpy (*thumb, match->data, match->data_size);
516 return match->data_size;
517}
518
519
520/**
521 * Duplicate a `struct GNUNET_FS_MetaData`.
522 *
523 * @param md what to duplicate
524 * @return duplicate meta-data container
525 */
526struct GNUNET_FS_MetaData *
527GNUNET_FS_meta_data_duplicate (const struct GNUNET_FS_MetaData
528 *md)
529{
530 struct GNUNET_FS_MetaData *ret;
531 struct MetaItem *pos;
532
533 if (NULL == md)
534 return NULL;
535 ret = GNUNET_FS_meta_data_create ();
536 for (pos = md->items_tail; NULL != pos; pos = pos->prev)
537 GNUNET_FS_meta_data_insert (ret, pos->plugin_name, pos->type,
538 pos->format, pos->mime_type, pos->data,
539 pos->data_size);
540 return ret;
541}
542
543
544/**
545 * Flag in 'version' that indicates compressed meta-data.
546 */
547#define HEADER_COMPRESSED 0x80000000
548
549
550/**
551 * Bits in 'version' that give the version number.
552 */
553#define HEADER_VERSION_MASK 0x7FFFFFFF
554
555
556/**
557 * Header for serialized meta data.
558 */
559struct MetaDataHeader
560{
561 /**
562 * The version of the MD serialization. The highest bit is used to
563 * indicate compression.
564 *
565 * Version 0 is traditional (pre-0.9) meta data (unsupported)
566 * Version is 1 for a NULL pointer
567 * Version 2 is for 0.9.x (and possibly higher)
568 * Other version numbers are not yet defined.
569 */
570 uint32_t version;
571
572 /**
573 * How many MD entries are there?
574 */
575 uint32_t entries;
576
577 /**
578 * Size of the decompressed meta data.
579 */
580 uint32_t size;
581
582 /**
583 * This is followed by 'entries' values of type 'struct MetaDataEntry'
584 * and then by 'entry' plugin names, mime-types and data blocks
585 * as specified in those meta data entries.
586 */
587};
588
589
590/**
591 * Entry of serialized meta data.
592 */
593struct MetaDataEntry
594{
595 /**
596 * Meta data type. Corresponds to an 'enum EXTRACTOR_MetaType'
597 */
598 uint32_t type;
599
600 /**
601 * Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat'
602 */
603 uint32_t format;
604
605 /**
606 * Number of bytes of meta data.
607 */
608 uint32_t data_size;
609
610 /**
611 * Number of bytes in the plugin name including 0-terminator. 0 for NULL.
612 */
613 uint32_t plugin_name_len;
614
615 /**
616 * Number of bytes in the mime type including 0-terminator. 0 for NULL.
617 */
618 uint32_t mime_type_len;
619};
620
621
622/**
623 * Serialize meta-data to target.
624 *
625 * @param md metadata to serialize
626 * @param target where to write the serialized metadata;
627 * *target can be NULL, in which case memory is allocated
628 * @param max maximum number of bytes available in target
629 * @param opt is it ok to just write SOME of the
630 * meta-data to match the size constraint,
631 * possibly discarding some data?
632 * @return number of bytes written on success,
633 * #GNUNET_SYSERR on error (typically: not enough
634 * space)
635 */
636ssize_t
637GNUNET_FS_meta_data_serialize (const struct GNUNET_FS_MetaData
638 *md, char **target, size_t max,
639 enum
640 GNUNET_FS_MetaDataSerializationOptions
641 opt)
642{
643 struct GNUNET_FS_MetaData *vmd;
644 struct MetaItem *pos;
645 struct MetaDataHeader ihdr;
646 struct MetaDataHeader *hdr;
647 struct MetaDataEntry *ent;
648 char *dst;
649 unsigned int i;
650 uint64_t msize;
651 size_t off;
652 char *mdata;
653 char *cdata;
654 size_t mlen;
655 size_t plen;
656 size_t size;
657 size_t left;
658 size_t clen;
659 size_t rlen;
660 int comp;
661
662 if (max < sizeof(struct MetaDataHeader))
663 return GNUNET_SYSERR; /* far too small */
664 if (NULL == md)
665 return 0;
666
667 if (NULL != md->sbuf)
668 {
669 /* try to use serialization cache */
670 if (md->sbuf_size <= max)
671 {
672 if (NULL == *target)
673 *target = GNUNET_malloc (md->sbuf_size);
674 GNUNET_memcpy (*target, md->sbuf, md->sbuf_size);
675 return md->sbuf_size;
676 }
677 if (0 == (opt & GNUNET_FS_META_DATA_SERIALIZE_PART))
678 return GNUNET_SYSERR; /* can say that this will fail */
679 /* need to compute a partial serialization, sbuf useless ... */
680 }
681 dst = NULL;
682 msize = 0;
683 for (pos = md->items_tail; NULL != pos; pos = pos->prev)
684 {
685 msize += sizeof(struct MetaDataEntry);
686 msize += pos->data_size;
687 if (NULL != pos->plugin_name)
688 msize += strlen (pos->plugin_name) + 1;
689 if (NULL != pos->mime_type)
690 msize += strlen (pos->mime_type) + 1;
691 }
692 size = (size_t) msize;
693 if (size != msize)
694 {
695 GNUNET_break (0); /* integer overflow */
696 return GNUNET_SYSERR;
697 }
698 if (size >= GNUNET_MAX_MALLOC_CHECKED)
699 {
700 /* too large to be processed */
701 return GNUNET_SYSERR;
702 }
703 ent = GNUNET_malloc (size);
704 mdata = (char *) &ent[md->item_count];
705 off = size - (md->item_count * sizeof(struct MetaDataEntry));
706 i = 0;
707 for (pos = md->items_head; NULL != pos; pos = pos->next)
708 {
709 ent[i].type = htonl ((uint32_t) pos->type);
710 ent[i].format = htonl ((uint32_t) pos->format);
711 ent[i].data_size = htonl ((uint32_t) pos->data_size);
712 if (NULL == pos->plugin_name)
713 plen = 0;
714 else
715 plen = strlen (pos->plugin_name) + 1;
716 ent[i].plugin_name_len = htonl ((uint32_t) plen);
717 if (NULL == pos->mime_type)
718 mlen = 0;
719 else
720 mlen = strlen (pos->mime_type) + 1;
721 ent[i].mime_type_len = htonl ((uint32_t) mlen);
722 off -= pos->data_size;
723 if ((EXTRACTOR_METAFORMAT_UTF8 == pos->format) ||
724 (EXTRACTOR_METAFORMAT_C_STRING == pos->format))
725 GNUNET_break ('\0' == pos->data[pos->data_size - 1]);
726 GNUNET_memcpy (&mdata[off], pos->data, pos->data_size);
727 off -= plen;
728 if (NULL != pos->plugin_name)
729 GNUNET_memcpy (&mdata[off], pos->plugin_name, plen);
730 off -= mlen;
731 if (NULL != pos->mime_type)
732 GNUNET_memcpy (&mdata[off], pos->mime_type, mlen);
733 i++;
734 }
735 GNUNET_assert (0 == off);
736
737 clen = 0;
738 cdata = NULL;
739 left = size;
740 i = 0;
741 for (pos = md->items_head; NULL != pos; pos = pos->next)
742 {
743 comp = GNUNET_NO;
744 if (0 == (opt & GNUNET_FS_META_DATA_SERIALIZE_NO_COMPRESS))
745 comp = GNUNET_try_compression ((const char *) &ent[i],
746 left,
747 &cdata,
748 &clen);
749
750 if ((NULL == md->sbuf) && (0 == i))
751 {
752 /* fill 'sbuf'; this "modifies" md, but since this is only
753 * an internal cache we will cast away the 'const' instead
754 * of making the API look strange. */
755 vmd = (struct GNUNET_FS_MetaData *) md;
756 hdr = GNUNET_malloc (left + sizeof(struct MetaDataHeader));
757 hdr->size = htonl (left);
758 hdr->entries = htonl (md->item_count);
759 if (GNUNET_YES == comp)
760 {
761 GNUNET_assert (clen < left);
762 hdr->version = htonl (2 | HEADER_COMPRESSED);
763 GNUNET_memcpy (&hdr[1], cdata, clen);
764 vmd->sbuf_size = clen + sizeof(struct MetaDataHeader);
765 }
766 else
767 {
768 hdr->version = htonl (2);
769 GNUNET_memcpy (&hdr[1], &ent[0], left);
770 vmd->sbuf_size = left + sizeof(struct MetaDataHeader);
771 }
772 vmd->sbuf = (char *) hdr;
773 }
774
775 if (((left + sizeof(struct MetaDataHeader)) <= max) ||
776 ((GNUNET_YES == comp) && (clen <= max)))
777 {
778 /* success, this now fits! */
779 if (GNUNET_YES == comp)
780 {
781 if (NULL == dst)
782 dst = GNUNET_malloc (clen + sizeof(struct MetaDataHeader));
783 hdr = (struct MetaDataHeader *) dst;
784 hdr->version = htonl (2 | HEADER_COMPRESSED);
785 hdr->size = htonl (left);
786 hdr->entries = htonl (md->item_count - i);
787 GNUNET_memcpy (&dst[sizeof(struct MetaDataHeader)], cdata, clen);
788 GNUNET_free (cdata);
789 cdata = NULL;
790 GNUNET_free (ent);
791 rlen = clen + sizeof(struct MetaDataHeader);
792 }
793 else
794 {
795 if (NULL == dst)
796 dst = GNUNET_malloc (left + sizeof(struct MetaDataHeader));
797 hdr = (struct MetaDataHeader *) dst;
798 hdr->version = htonl (2);
799 hdr->entries = htonl (md->item_count - i);
800 hdr->size = htonl (left);
801 GNUNET_memcpy (&dst[sizeof(struct MetaDataHeader)], &ent[i], left);
802 GNUNET_free (ent);
803 rlen = left + sizeof(struct MetaDataHeader);
804 }
805 if (NULL != *target)
806 {
807 if (GNUNET_YES == comp)
808 GNUNET_memcpy (*target, dst, clen + sizeof(struct MetaDataHeader));
809 else
810 GNUNET_memcpy (*target, dst, left + sizeof(struct MetaDataHeader));
811 GNUNET_free (dst);
812 }
813 else
814 {
815 *target = dst;
816 }
817 return rlen;
818 }
819
820 if (0 == (opt & GNUNET_FS_META_DATA_SERIALIZE_PART))
821 {
822 /* does not fit! */
823 GNUNET_free (ent);
824 if (NULL != cdata)
825 GNUNET_free (cdata);
826 cdata = NULL;
827 return GNUNET_SYSERR;
828 }
829
830 /* next iteration: ignore the corresponding meta data at the
831 * end and try again without it */
832 left -= sizeof(struct MetaDataEntry);
833 left -= pos->data_size;
834 if (NULL != pos->plugin_name)
835 left -= strlen (pos->plugin_name) + 1;
836 if (NULL != pos->mime_type)
837 left -= strlen (pos->mime_type) + 1;
838
839 if (NULL != cdata)
840 GNUNET_free (cdata);
841 cdata = NULL;
842 i++;
843 }
844 GNUNET_free (ent);
845
846 /* nothing fit, only write header! */
847 ihdr.version = htonl (2);
848 ihdr.entries = htonl (0);
849 ihdr.size = htonl (0);
850 if (NULL == *target)
851 *target = (char *) GNUNET_new (struct MetaDataHeader);
852 GNUNET_memcpy (*target, &ihdr, sizeof(struct MetaDataHeader));
853 return sizeof(struct MetaDataHeader);
854}
855
856
857ssize_t
858GNUNET_FS_meta_data_get_serialized_size (const struct
859 GNUNET_FS_MetaData *md)
860{
861 ssize_t ret;
862 char *ptr;
863
864 if (NULL != md->sbuf)
865 return md->sbuf_size;
866 ptr = NULL;
867 ret =
868 GNUNET_FS_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED,
869 GNUNET_FS_META_DATA_SERIALIZE_FULL);
870 if (-1 != ret)
871 GNUNET_free (ptr);
872 return ret;
873}
874
875
876/**
877 * Deserialize meta-data. Initializes md.
878 *
879 * @param input buffer with the serialized metadata
880 * @param size number of bytes available in input
881 * @return MD on success, NULL on error (i.e.
882 * bad format)
883 */
884struct GNUNET_FS_MetaData *
885GNUNET_FS_meta_data_deserialize (const char *input, size_t size)
886{
887 struct GNUNET_FS_MetaData *md;
888 struct MetaDataHeader hdr;
889 struct MetaDataEntry ent;
890 uint32_t ic;
891 uint32_t i;
892 char *data;
893 const char *cdata;
894 uint32_t version;
895 uint32_t dataSize;
896 int compressed;
897 size_t left;
898 uint32_t mlen;
899 uint32_t plen;
900 uint32_t dlen;
901 const char *mdata;
902 const char *meta_data;
903 const char *plugin_name;
904 const char *mime_type;
905 enum EXTRACTOR_MetaFormat format;
906
907 if (size < sizeof(struct MetaDataHeader))
908 return NULL;
909 GNUNET_memcpy (&hdr, input, sizeof(struct MetaDataHeader));
910 version = ntohl (hdr.version) & HEADER_VERSION_MASK;
911 compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
912
913 if (1 == version)
914 return NULL; /* null pointer */
915 if (2 != version)
916 {
917 GNUNET_break_op (0); /* unsupported version */
918 return NULL;
919 }
920
921 ic = ntohl (hdr.entries);
922 dataSize = ntohl (hdr.size);
923 if (((sizeof(struct MetaDataEntry) * ic) > dataSize) ||
924 ((0 != ic) &&
925 (dataSize / ic < sizeof(struct MetaDataEntry))))
926 {
927 GNUNET_break_op (0);
928 return NULL;
929 }
930
931 if (compressed)
932 {
933 if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
934 {
935 /* make sure we don't blow our memory limit because of a mal-formed
936 * message... */
937 GNUNET_break_op (0);
938 return NULL;
939 }
940 data =
941 GNUNET_decompress ((const char *) &input[sizeof(struct MetaDataHeader)],
942 size - sizeof(struct MetaDataHeader),
943 dataSize);
944 if (NULL == data)
945 {
946 GNUNET_break_op (0);
947 return NULL;
948 }
949 cdata = data;
950 }
951 else
952 {
953 data = NULL;
954 cdata = (const char *) &input[sizeof(struct MetaDataHeader)];
955 if (dataSize != size - sizeof(struct MetaDataHeader))
956 {
957 GNUNET_break_op (0);
958 return NULL;
959 }
960 }
961
962 md = GNUNET_FS_meta_data_create ();
963 left = dataSize - ic * sizeof(struct MetaDataEntry);
964 mdata = &cdata[ic * sizeof(struct MetaDataEntry)];
965 for (i = 0; i < ic; i++)
966 {
967 GNUNET_memcpy (&ent, &cdata[i * sizeof(struct MetaDataEntry)],
968 sizeof(struct MetaDataEntry));
969 format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
970 if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
971 (EXTRACTOR_METAFORMAT_C_STRING != format) &&
972 (EXTRACTOR_METAFORMAT_BINARY != format))
973 {
974 GNUNET_break_op (0);
975 break;
976 }
977 dlen = ntohl (ent.data_size);
978 plen = ntohl (ent.plugin_name_len);
979 mlen = ntohl (ent.mime_type_len);
980 if (dlen > left)
981 {
982 GNUNET_break_op (0);
983 break;
984 }
985 left -= dlen;
986 meta_data = &mdata[left];
987 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
988 (EXTRACTOR_METAFORMAT_C_STRING == format))
989 {
990 if (0 == dlen)
991 {
992 GNUNET_break_op (0);
993 break;
994 }
995 if ('\0' != meta_data[dlen - 1])
996 {
997 GNUNET_break_op (0);
998 break;
999 }
1000 }
1001 if (plen > left)
1002 {
1003 GNUNET_break_op (0);
1004 break;
1005 }
1006 left -= plen;
1007 if ((plen > 0) && ('\0' != mdata[left + plen - 1]))
1008 {
1009 GNUNET_break_op (0);
1010 break;
1011 }
1012 if (0 == plen)
1013 plugin_name = NULL;
1014 else
1015 plugin_name = &mdata[left];
1016
1017 if (mlen > left)
1018 {
1019 GNUNET_break_op (0);
1020 break;
1021 }
1022 left -= mlen;
1023 if ((mlen > 0) && ('\0' != mdata[left + mlen - 1]))
1024 {
1025 GNUNET_break_op (0);
1026 break;
1027 }
1028 if (0 == mlen)
1029 mime_type = NULL;
1030 else
1031 mime_type = &mdata[left];
1032 GNUNET_FS_meta_data_insert (md, plugin_name,
1033 (enum EXTRACTOR_MetaType)
1034 ntohl (ent.type), format, mime_type,
1035 meta_data, dlen);
1036 }
1037 GNUNET_free (data);
1038 return md;
1039}
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/**
1102 * Write a metadata container.
1103 *
1104 * @param h the IO handle to write to
1105 * @param what what is being written (for error message creation)
1106 * @param m metadata to write
1107 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1108 */
1109enum GNUNET_GenericReturnValue
1110GNUNET_FS_write_meta_data (struct GNUNET_BIO_WriteHandle *h,
1111 const char *what,
1112 const struct GNUNET_FS_MetaData *m)
1113{
1114 ssize_t size;
1115 char *buf;
1116
1117 if (m == NULL)
1118 return GNUNET_BIO_write_int32 (h, _ ("metadata length"), 0);
1119 buf = NULL;
1120 size = GNUNET_FS_meta_data_serialize (m,
1121 &buf,
1122 MAX_META_DATA,
1123 GNUNET_FS_META_DATA_SERIALIZE_PART);
1124 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1125 "Serialized %lld bytes of metadata",
1126 (long long) size);
1127
1128 if (-1 == size)
1129 {
1130 GNUNET_free (buf);
1131 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1132 "Failed to serialize metadata `%s'",
1133 what);
1134 return GNUNET_SYSERR;
1135 }
1136 if ( (GNUNET_OK !=
1137 GNUNET_BIO_write_int32 (h,
1138 "metadata length",
1139 (uint32_t) size)) ||
1140 (GNUNET_OK !=
1141 GNUNET_BIO_write (h,
1142 what,
1143 buf,
1144 size)) )
1145 {
1146 GNUNET_free (buf);
1147 return GNUNET_SYSERR;
1148 }
1149 GNUNET_free (buf);
1150 return GNUNET_OK;
1151}
1152
1153
1154/**
1155 * Function used internally to read a metadata container from within a read
1156 * spec.
1157 *
1158 * @param cls ignored, always NULL
1159 * @param h the IO handle to read from
1160 * @param what what is being read (for error message creation)
1161 * @param target where to store the data
1162 * @param target_size ignored
1163 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1164 */
1165static int
1166read_spec_handler_meta_data (void *cls,
1167 struct GNUNET_BIO_ReadHandle *h,
1168 const char *what,
1169 void *target,
1170 size_t target_size)
1171{
1172 struct GNUNET_FS_MetaData **result = target;
1173 return GNUNET_FS_read_meta_data (h, what, result);
1174}
1175
1176
1177/**
1178 * Create the specification to read a metadata container.
1179 *
1180 * @param what describes what is being read (for error message creation)
1181 * @param result the buffer to store a pointer to the (allocated) metadata
1182 * @return the read spec
1183 */
1184struct GNUNET_BIO_ReadSpec
1185GNUNET_FS_read_spec_meta_data (const char *what,
1186 struct GNUNET_FS_MetaData **result)
1187{
1188 struct GNUNET_BIO_ReadSpec rs = {
1189 .rh = &read_spec_handler_meta_data,
1190 .cls = NULL,
1191 .target = result,
1192 .size = 0,
1193 };
1194
1195 return rs;
1196}
1197
1198
1199/**
1200 * Function used internally to write a metadata container from within a write
1201 * spec.
1202 *
1203 * @param cls ignored, always NULL
1204 * @param h the IO handle to write to
1205 * @param what what is being written (for error message creation)
1206 * @param source the data to write
1207 * @param source_size ignored
1208 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1209 */
1210static int
1211write_spec_handler_meta_data (void *cls,
1212 struct GNUNET_BIO_WriteHandle *h,
1213 const char *what,
1214 void *source,
1215 size_t source_size)
1216{
1217 const struct GNUNET_FS_MetaData *m = source;
1218 return GNUNET_FS_write_meta_data (h, what, m);
1219}
1220
1221
1222struct GNUNET_BIO_WriteSpec
1223GNUNET_FS_write_spec_meta_data (const char *what,
1224 const struct GNUNET_FS_MetaData *m)
1225{
1226 struct GNUNET_BIO_WriteSpec ws = {
1227 .wh = &write_spec_handler_meta_data,
1228 .cls = NULL,
1229 .what = what,
1230 .source = (void *) m,
1231 .source_size = 0,
1232 };
1233
1234 return ws;
1235}
1236
1237
1238/* end of meta_data.c */
diff --git a/src/service/fs/perf_gnunet_service_fs_p2p.c b/src/service/fs/perf_gnunet_service_fs_p2p.c
new file mode 100644
index 000000000..2c7830f5f
--- /dev/null
+++ b/src/service/fs/perf_gnunet_service_fs_p2p.c
@@ -0,0 +1,370 @@
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/service/fs/perf_gnunet_service_fs_p2p.conf b/src/service/fs/perf_gnunet_service_fs_p2p.conf
new file mode 100644
index 000000000..00f0f512e
--- /dev/null
+++ b/src/service/fs/perf_gnunet_service_fs_p2p.conf
@@ -0,0 +1,7 @@
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/service/fs/perf_gnunet_service_fs_p2p_respect.c b/src/service/fs/perf_gnunet_service_fs_p2p_respect.c
new file mode 100644
index 000000000..6b71b1f93
--- /dev/null
+++ b/src/service/fs/perf_gnunet_service_fs_p2p_respect.c
@@ -0,0 +1,480 @@
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/service/fs/test_fs.c b/src/service/fs/test_fs.c
new file mode 100644
index 000000000..7a57e98b0
--- /dev/null
+++ b/src/service/fs/test_fs.c
@@ -0,0 +1,262 @@
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/service/fs/test_fs_data.conf b/src/service/fs/test_fs_data.conf
new file mode 100644
index 000000000..993141064
--- /dev/null
+++ b/src/service/fs/test_fs_data.conf
@@ -0,0 +1,7 @@
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/service/fs/test_fs_defaults.conf b/src/service/fs/test_fs_defaults.conf
new file mode 100644
index 000000000..74456c4a2
--- /dev/null
+++ b/src/service/fs/test_fs_defaults.conf
@@ -0,0 +1,47 @@
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/service/fs/test_fs_directory.c b/src/service/fs/test_fs_directory.c
new file mode 100644
index 000000000..f5121a3e7
--- /dev/null
+++ b/src/service/fs/test_fs_directory.c
@@ -0,0 +1,186 @@
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/service/fs/test_fs_download.c b/src/service/fs/test_fs_download.c
new file mode 100644
index 000000000..fc6b32c0f
--- /dev/null
+++ b/src/service/fs/test_fs_download.c
@@ -0,0 +1,368 @@
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/service/fs/test_fs_download_data.conf b/src/service/fs/test_fs_download_data.conf
new file mode 100644
index 000000000..160dec3ae
--- /dev/null
+++ b/src/service/fs/test_fs_download_data.conf
@@ -0,0 +1,10 @@
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/service/fs/test_fs_download_indexed.conf b/src/service/fs/test_fs_download_indexed.conf
new file mode 100644
index 000000000..7f1e36935
--- /dev/null
+++ b/src/service/fs/test_fs_download_indexed.conf
@@ -0,0 +1,10 @@
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/service/fs/test_fs_download_persistence.c b/src/service/fs/test_fs_download_persistence.c
new file mode 100644
index 000000000..b66fefd6b
--- /dev/null
+++ b/src/service/fs/test_fs_download_persistence.c
@@ -0,0 +1,351 @@
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/service/fs/test_fs_file_information.c b/src/service/fs/test_fs_file_information.c
new file mode 100644
index 000000000..15380bfc4
--- /dev/null
+++ b/src/service/fs/test_fs_file_information.c
@@ -0,0 +1,163 @@
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/service/fs/test_fs_file_information_data.conf b/src/service/fs/test_fs_file_information_data.conf
new file mode 100644
index 000000000..c8fc0938c
--- /dev/null
+++ b/src/service/fs/test_fs_file_information_data.conf
@@ -0,0 +1,7 @@
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/service/fs/test_fs_getopt.c b/src/service/fs/test_fs_getopt.c
new file mode 100644
index 000000000..3d0da752b
--- /dev/null
+++ b/src/service/fs/test_fs_getopt.c
@@ -0,0 +1,37 @@
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/service/fs/test_fs_list_indexed.c b/src/service/fs/test_fs_list_indexed.c
new file mode 100644
index 000000000..7e06c47f5
--- /dev/null
+++ b/src/service/fs/test_fs_list_indexed.c
@@ -0,0 +1,265 @@
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/service/fs/test_fs_list_indexed_data.conf b/src/service/fs/test_fs_list_indexed_data.conf
new file mode 100644
index 000000000..941809322
--- /dev/null
+++ b/src/service/fs/test_fs_list_indexed_data.conf
@@ -0,0 +1,10 @@
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/service/fs/test_fs_meta_data.c b/src/service/fs/test_fs_meta_data.c
new file mode 100644
index 000000000..4e7439d7b
--- /dev/null
+++ b/src/service/fs/test_fs_meta_data.c
@@ -0,0 +1,492 @@
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/service/fs/test_fs_namespace.c b/src/service/fs/test_fs_namespace.c
new file mode 100644
index 000000000..85d489598
--- /dev/null
+++ b/src/service/fs/test_fs_namespace.c
@@ -0,0 +1,320 @@
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/service/fs/test_fs_namespace_data.conf b/src/service/fs/test_fs_namespace_data.conf
new file mode 100644
index 000000000..70b954f7d
--- /dev/null
+++ b/src/service/fs/test_fs_namespace_data.conf
@@ -0,0 +1,7 @@
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/service/fs/test_fs_namespace_list_updateable.c b/src/service/fs/test_fs_namespace_list_updateable.c
new file mode 100644
index 000000000..d883b7bea
--- /dev/null
+++ b/src/service/fs/test_fs_namespace_list_updateable.c
@@ -0,0 +1,175 @@
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/service/fs/test_fs_publish.c b/src/service/fs/test_fs_publish.c
new file mode 100644
index 000000000..0e379bc29
--- /dev/null
+++ b/src/service/fs/test_fs_publish.c
@@ -0,0 +1,251 @@
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/service/fs/test_fs_publish_data.conf b/src/service/fs/test_fs_publish_data.conf
new file mode 100644
index 000000000..0930cdfed
--- /dev/null
+++ b/src/service/fs/test_fs_publish_data.conf
@@ -0,0 +1,10 @@
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/service/fs/test_fs_publish_persistence.c b/src/service/fs/test_fs_publish_persistence.c
new file mode 100644
index 000000000..e1563f448
--- /dev/null
+++ b/src/service/fs/test_fs_publish_persistence.c
@@ -0,0 +1,322 @@
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/service/fs/test_fs_search.c b/src/service/fs/test_fs_search.c
new file mode 100644
index 000000000..f9266582e
--- /dev/null
+++ b/src/service/fs/test_fs_search.c
@@ -0,0 +1,252 @@
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/service/fs/test_fs_search_data.conf b/src/service/fs/test_fs_search_data.conf
new file mode 100644
index 000000000..8b24e328d
--- /dev/null
+++ b/src/service/fs/test_fs_search_data.conf
@@ -0,0 +1,7 @@
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/service/fs/test_fs_search_persistence.c b/src/service/fs/test_fs_search_persistence.c
new file mode 100644
index 000000000..4ddd40e73
--- /dev/null
+++ b/src/service/fs/test_fs_search_persistence.c
@@ -0,0 +1,318 @@
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/service/fs/test_fs_search_probes.c b/src/service/fs/test_fs_search_probes.c
new file mode 100644
index 000000000..776babaee
--- /dev/null
+++ b/src/service/fs/test_fs_search_probes.c
@@ -0,0 +1,258 @@
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/service/fs/test_fs_search_with_and.c b/src/service/fs/test_fs_search_with_and.c
new file mode 100644
index 000000000..9c20936b6
--- /dev/null
+++ b/src/service/fs/test_fs_search_with_and.c
@@ -0,0 +1,272 @@
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/service/fs/test_fs_start_stop.c b/src/service/fs/test_fs_start_stop.c
new file mode 100644
index 000000000..509fbe76a
--- /dev/null
+++ b/src/service/fs/test_fs_start_stop.c
@@ -0,0 +1,64 @@
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/service/fs/test_fs_test_lib.c b/src/service/fs/test_fs_test_lib.c
new file mode 100644
index 000000000..714dd452e
--- /dev/null
+++ b/src/service/fs/test_fs_test_lib.c
@@ -0,0 +1,181 @@
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/service/fs/test_fs_unindex.c b/src/service/fs/test_fs_unindex.c
new file mode 100644
index 000000000..dbc33090d
--- /dev/null
+++ b/src/service/fs/test_fs_unindex.c
@@ -0,0 +1,237 @@
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/service/fs/test_fs_unindex_data.conf b/src/service/fs/test_fs_unindex_data.conf
new file mode 100644
index 000000000..dde401857
--- /dev/null
+++ b/src/service/fs/test_fs_unindex_data.conf
@@ -0,0 +1,7 @@
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/service/fs/test_fs_unindex_persistence.c b/src/service/fs/test_fs_unindex_persistence.c
new file mode 100644
index 000000000..b81ce64ab
--- /dev/null
+++ b/src/service/fs/test_fs_unindex_persistence.c
@@ -0,0 +1,307 @@
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/service/fs/test_fs_uri.c b/src/service/fs/test_fs_uri.c
new file mode 100644
index 000000000..e0f23097b
--- /dev/null
+++ b/src/service/fs/test_fs_uri.c
@@ -0,0 +1,340 @@
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/service/fs/test_gnunet_fs_rec_data.tgz b/src/service/fs/test_gnunet_fs_rec_data.tgz
new file mode 100644
index 000000000..697794306
--- /dev/null
+++ b/src/service/fs/test_gnunet_fs_rec_data.tgz
Binary files differ
diff --git a/src/service/fs/test_gnunet_service_fs_migration.c b/src/service/fs/test_gnunet_service_fs_migration.c
new file mode 100644
index 000000000..38b00f3e8
--- /dev/null
+++ b/src/service/fs/test_gnunet_service_fs_migration.c
@@ -0,0 +1,223 @@
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/service/fs/test_gnunet_service_fs_migration_data.conf b/src/service/fs/test_gnunet_service_fs_migration_data.conf
new file mode 100644
index 000000000..fca6c5a29
--- /dev/null
+++ b/src/service/fs/test_gnunet_service_fs_migration_data.conf
@@ -0,0 +1,10 @@
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/service/fs/test_gnunet_service_fs_p2p.c b/src/service/fs/test_gnunet_service_fs_p2p.c
new file mode 100644
index 000000000..2d1fbb788
--- /dev/null
+++ b/src/service/fs/test_gnunet_service_fs_p2p.c
@@ -0,0 +1,167 @@
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/service/fs/test_gnunet_service_fs_p2p_cadet.conf b/src/service/fs/test_gnunet_service_fs_p2p_cadet.conf
new file mode 100644
index 000000000..12e106968
--- /dev/null
+++ b/src/service/fs/test_gnunet_service_fs_p2p_cadet.conf
@@ -0,0 +1,20 @@
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/service/fs/test_pseudonym_data.conf b/src/service/fs/test_pseudonym_data.conf
new file mode 100644
index 000000000..5827721b8
--- /dev/null
+++ b/src/service/fs/test_pseudonym_data.conf
@@ -0,0 +1,6 @@
1# General settings
2[fs]
3
4[TESTING]
5WEAKRANDOM = YES
6