1 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 open DonkeyComplexOptions
34 module VB
= VerificationBitmap
36 let must_share_file file codedname has_old_impl
=
37 match file
.file_shared
with
40 let full_name = file_disk_name file
in
41 check_magic
(as_file file
);
43 impl_shared_update
= 1;
44 impl_shared_fullname
= full_name;
45 impl_shared_codedname
= codedname
;
46 impl_shared_size
= file_size file
;
47 impl_shared_id
= file
.file_md4
;
49 impl_shared_uploaded
= Int64.zero
;
50 impl_shared_ops
= shared_ops
;
51 impl_shared_val
= file
;
52 impl_shared_requests
= 0;
53 impl_shared_file
= Some
(as_file file
);
54 impl_shared_servers
= []
56 file
.file_shared
<- Some
impl;
57 incr
CommonGlobals.nshared_files
;
58 CommonShared.shared_calculate_total_bytes
();
59 match has_old_impl
with
60 None
-> update_shared_num
impl
61 | Some old_impl
-> replace_shared old_impl
impl
63 let new_file_to_share sh codedname old_impl
=
65 (* How do we compute the total MD4 of the file ? *)
67 let md4s = sh
.sh_md4s
in
68 let md4 = match Array.length
md4s with
71 | _
-> md4_of_array
md4s
74 if !verbose_share
then
75 lprintf_nl
"Sharing file with MD4: %s" (Md4.to_string
md4);
77 let user = CommonUserDb.admin_user
() in
78 let file = new_file sh
.sh_name FileShared
md4 sh
.sh_size
79 "" false user user.user_default_group
in
80 must_share_file file codedname old_impl
;
81 file.file_computed_md4s
<- md4s;
82 add_file_filenames
(as_file
file) (Filename.basename sh
.sh_name
);
83 update_best_name
file;
84 (* file.file_chunks <- Array.make file.file_nchunks PresentVerified; *)
85 (* file.file_absent_chunks <- []; *)
86 (* file.file_all_chunks <- String.make file.file_nchunks '1'; *)
87 (* Should we trust mtimes, or reverify each file. If we trust
88 * mtimes, I guess we have to call
89 * CommonSwarming.set_chunks_verified_bitmap "333..."
90 * this seems unspeakably ugly, but the alternative is to reverify
91 * every shared file every hour.
93 * If, however, we could somehow avoid regenerating shared files
94 * when the directory is scanned, verifying everything on startup
95 * might be acceptable.
97 * Also, timestamps would be more resilient if we took the maximum
98 * of mtime and ctime. (Touch will set ctime to the current time
99 * regardless of the mtime being set.)
101 match file.file_swarmer
with
103 let len = Array.length
md4s in
104 let ver_str = String.make
len (VB.state_to_char
VB.State_verified
) in
105 CommonSwarming.set_chunks_verified_bitmap s
106 (VB.of_string
ver_str);
108 CommonSwarming.set_present s [(Int64.zero, file_size file)];
109 (* If we don't verify now, it will never happen! *)
110 CommonSwarming.verify_all_blocks s
true;
112 if !verbose_share
then
113 lprintf_nl
"verified map of %s = %s"
114 (codedname
) (VB.to_string
(CommonSwarming.chunks_verified_bitmap s
))
116 if !verbose_share
then
117 lprintf_nl
"no swarmer for %s" codedname
;
119 file.file_format
<- CommonMultimedia.get_info
120 (file_disk_name
file)
124 DonkeyOvernet.publish_file file
126 lprintf "DonkeyOvernet.publish_file: %s" (Printexc2.to_string e);
129 if !verbose_share
then
130 lprintf_nl
"new_file_to_share: Sharing %s" sh
.sh_name
;
132 lprintf_nl
"Exception %s while sharing %s" (Printexc2.to_string e
)
136 let shared_files = ref [] in
137 Hashtbl.iter
(fun md4 file ->
138 match file.file_shared
with
140 | Some _
-> shared_files := file :: !shared_files
142 if !verbose_share
then lprintf_nl
"scanned shared files, %d files found" (List.length
!shared_files);
145 (* publish shared files to servers, called once per minute *)
146 let send_new_shared () =
147 (* sort list to publish least published files first *)
148 let ( |> ) x f
= f x
in
151 |> List.map
(fun e
->
152 (match e
.file_shared
with
153 Some s
-> List.length s
.impl_shared_servers
155 |> List.sort
(fun (a
,_
) (b
,_
) -> compare a b
)
159 (* iter through connected servers *)
162 (* publish files only on master servers and do not publish more files than hard limit allows *)
163 if s
.server_master
&&
164 (match s
.server_hard_limit
with
165 Some v
when (Int64.to_int v
) < List.length s
.server_sent_shared
-> false
168 (* iter through all shared files and check if the file is already published on the current server
169 build a list of files_to_send with yet unpublished files *)
171 let files_to_send = ref [] in
172 let can_publish f
= not
(file_is_largefile f
&& not s
.server_has_largefiles
) in
174 match f
.file_shared
with
176 if not
(List.mem
(CommonServer.as_server s
.server_server
) impl.impl_shared_servers
)
177 && List.length
!files_to_send < !!max_published_files
&& can_publish f
then
178 files_to_send := f
:: !files_to_send
180 if not
(can_publish f
) then
181 lprintf_nl
"Can not publish largefile %s because server %s does not support largefiles"
182 (file_best_name f
) (string_of_server s
)
183 | _
-> () (* this case never happens *)
186 if !files_to_send <> [] then
188 if !verbose_share
|| !verbose
then
189 lprintf_nl
"publishing %d new files to %s (holds %d files)"
190 (List.length
!files_to_send) (string_of_server s
)
191 (List.length s
.server_sent_shared
);
193 (* publish files on server *)
194 do_if_connected s
.server_sock
(fun sock
->
195 server_send_share s
.server_has_zlib sock
!files_to_send);
197 (* append new published files to server structure *)
198 s
.server_sent_shared
<- !files_to_send @ s
.server_sent_shared
;
200 (* iter through published files and append current server *)
201 List.iter
(fun file ->
202 match file.file_shared
with
203 Some
impl -> impl.impl_shared_servers
<-
204 impl.impl_shared_servers
@ [(CommonServer.as_server s
.server_server
)]
205 | _
-> ()) !files_to_send
209 ) (logged_in_servers
());
212 The problem: sh.shared_fd might be closed during the execution of the
213 thread. Moreover, we don't want to open all the filedescs for all the
217 exception Wrong_file_size
of int64
* int64
219 let computing = ref false
221 (* Compute (at most) one MD4 chunk if needed. *)
222 let rec check_shared_files () =
223 let module M
= CommonHasher
in
224 if not
!computing then
225 match !shared_files with
228 let rec job_creater _
=
230 if not
(Sys.file_exists sh
.shared_name
) then
232 if Unix32.getsize sh
.shared_name
<> sh
.shared_size
then
233 raise
(Wrong_file_size
((Unix32.getsize sh
.shared_name
), sh
.shared_size
));
237 let end_pos = sh
.shared_pos
++ block_size
in
238 let end_pos = min
end_pos sh
.shared_size
in
239 let len = end_pos -- sh
.shared_pos
in
242 lprintf_nl
"Hashing chunk %d: %Ld-%Ld (%Ld) of %s"
243 sh
.shared_chunk sh
.shared_pos
end_pos len sh
.shared_name
;
245 M.compute_md4
(Unix32.filename sh
.shared_fd
) sh
.shared_pos
len
248 if job
.M.job_error
then begin
249 lprintf_nl
"Error prevent sharing %s" sh
.shared_name
251 let new_md4 = job
.M.job_result
in
253 sh
.shared_list
<- new_md4 :: sh
.shared_list
;
254 sh
.shared_pos
<- end_pos;
255 sh
.shared_chunk
<- sh
.shared_chunk
+ 1;
257 if sh
.shared_chunk
= get_nchunks sh
.shared_size
then begin
259 sh_name
= sh
.shared_name
;
260 sh_size
= sh
.shared_size
;
261 sh_md4s
= Array.of_list
(List.rev sh
.shared_list
);
262 sh_mtime
= Unix32.mtime sh
.shared_name
;
264 lprintf_nl
"New shared file %s" sh
.shared_name
;
265 Hashtbl.add shared_files_info
266 (s.sh_name
, s.sh_size
, s.sh_mtime
) s;
267 known_shared_files
=:= s :: !!known_shared_files
;
268 shared_files := files
;
269 new_file_to_share s sh
.shared_shared
.impl_shared_codedname
(Some sh
.shared_shared
);
273 (* only try back-to-back hashing if hashing is
274 handled by a separate thread *)
275 check_shared_files ()
278 Wrong_file_size
(real,computed
) ->
279 shared_files := files
;
280 lprintf_nl
"Computed filesize %Ld does not match physical filesize %Ld, %s not shared"
281 computed
real sh
.shared_name
283 shared_files := files
;
284 lprintf_nl
"Exception %s prevents sharing of %s"
285 (Printexc2.to_string e
) sh
.shared_name
290 network
.op_network_share
<- (fun fullname codedname size
->
291 if !verbose_share
then
292 lprintf_nl
"op_network_share: Sharing %s" fullname
;
295 lprintf "Searching %s" fullname; lprint_newline ();
297 let mtime = Unix32.mtime fullname
in
299 let s = Hashtbl.find shared_files_info
300 (fullname
, size
, mtime) in
301 (* if s.sh_mtime = mtime && s.sh_size = size then begin *)
302 if !verbose_share
then
303 lprintf_nl
"donkeyShare: Using old MD4s for %s" fullname
;
304 new_file_to_share s codedname None
306 if !verbose_share then begin
307 lprintf "Shared file %s has been modified\n" fullname;
309 Hashtbl.remove shared_files_info fullname;
310 known_shared_files =:= List2.removeq s !!known_shared_files;
314 if !verbose_share
then
315 lprintf_nl
"donkeyShare: No info on %s" fullname
;
317 let found = ref false in
318 List.iter
(fun sh
-> if sh
.shared_name
= fullname
then found := true) !shared_files;
319 if not
!found then begin
321 impl_shared_update
= 1;
322 impl_shared_fullname
= fullname
;
323 impl_shared_codedname
= codedname
;
324 impl_shared_size
= size
;
326 impl_shared_uploaded
= Int64.zero
;
327 impl_shared_ops
= pre_shared_ops
;
328 impl_shared_id
= Md4.null
;
329 impl_shared_val
= pre_shared
;
330 impl_shared_requests
= 0;
331 impl_shared_file
= None
;
332 impl_shared_servers
= [];
335 shared_shared
= impl;
336 shared_name
= fullname
;
341 shared_fd
= Unix32.create_ro fullname
;
343 update_shared_num
impl;
344 shared_files := pre_shared
:: !shared_files;
348 let remember_shared_info file new_name
=
349 (* changed 2.5.24: normalization of file names is done in
350 CommonShared.new_shared, so it should be done here too. *)
351 let new_name = Filename2.normalize
new_name in
352 (* lprintf "****** remember_shared_info for new file %s\n" new_name; *)
353 if file.file_computed_md4s
<> [||] then
357 let disk_name = file_disk_name
file in
358 Unix32.mtime disk_name
361 lprintf_nl
"Share: Trying mtime on new name %s, disk_name %s"
362 new_name (file_disk_name
file);
363 Unix32.mtime new_name
366 if !verbose_share
then
367 lprintf_nl
"Remember %s" new_name;
369 let size = file_size
file in
370 if not
(Hashtbl.mem shared_files_info
371 (new_name, size, mtime) ) then
376 sh_md4s
= file.file_computed_md4s
;
379 known_shared_files
=:= s :: !!known_shared_files
;
380 Hashtbl.add shared_files_info
(new_name, size, mtime) s
382 lprintf_nl
"Exception %s in remember_shared_info"
383 (Printexc2.to_string e
)
385 let must_share_file file = must_share_file file (file_best_name
file) None