DevdocCommit
From MLDonkey
What is happening when a downloading file finishes? First of all some notes: "!!xxx" means an option or other data which is stored in MLDonkeys ini files, for example: !!temp_directory, which can be found in downloads.ini. Well, how does it all begin? Donkey============================================================================================= A timer is created in donkeyMain.ml that checks every !!compute_md4_delay whether a file is complete: add_session_option_timer enabler compute_md4_delay (fun _ -> DonkeyOneFile.check_files_downloaded (); DonkeyShare.check_shared_files () ); DonkeyOneFile.check_files_downloaded () - goes through all downloading files and calls donkeyOneFile.check_file_downloaded donkeyOneFile.check_file_downloaded - checks with the swarmer if all chunks have status 3 (complete and verified) - checks with the swarmer if the downloaded data matches the file size - calls donkeyOneFile.download_finished donkeyOneFile.download_finished - calls donkeyOneFile.declare_completed_file donkeyOneFile.declare_completed_file - among other things most important calls commonInteractive.file_completed commonInteractive.file_completed - works only on files with state FileDownloading - file is moved from !!files to !!done_files (these are sections in files.ini) - file receives state FileDownloaded - CommonShared.new_shared is called - file completion mail is send CommonShared.new_shared - puts the file into hashtable dirnames - iters through all network modules and calls CommonNetwork.network_share CommonNetwork.network_share - calls op_network_share in all network modules donkeyShare.ml let _ = network.op_network_share <- ( The following code is called on op_network_share. The donkey module has its own list of shared files besides commonShared.ml. This is a bit confusing and should be changed in the future, shared_files_new.ini is maintained only by donkeyShare.ml, shared_files.ini is maintained by Gnutella/G2/Fasttrack(GGF)-module. Here update_shared_num is called which is again in commonShared: commonShared.update_shared_num - adds the file in shareds_by_num shareds_by_num is a hashtable in commonShared which is, for example, used by the "upstats" command to display the list of shared files: driverCommands.ml "upstats", Arg_none (fun o -> let list = ref [] in shared_iter (fun s -> A list named "list" is created which is filled with the results of shared_iter which gives the results of shareds_by_num: commonShared.shared_iter f = H.iter f shareds_by_num shareds_by_num is a so called "weak hashtable" which means it is only available from the module where it is created, here it is commonShared.ml. It therefore can not be accessed directly from driverCommands.ml, so shared_iter is used for this. The next command in the timer in donkeyMain.ml is "DonkeyShare.check_shared_files ()" but it works only on committed files. Committing means *moving* the physical file from the !!temp_directory to the incoming directory. This will be explained later on BitTorrent========================================================================================== Another timer is created in bTMain.ml add_session_timer enabler 60.0 (fun timer -> BTClients.recover_files (); ); bTClients.recover_files () - calls check_finished bTClients.check_finished - checks with the swarmer if the file is finished, then calls bTClients.download_finished if Int64Swarmer.check_finished swarmer then download_finished file bTClients.download_finished - notifies the tracker that the file is finished - calls commonInteractive.file_completed (see notes in the Donkey section) op_network_share - does nothing here. The finished file(s) are still in !!temp_directory waiting for commit ==================================================================================================== committing files ==================================================================================================== Committing takes place either automatically (!!auto_commit true, files are checked every minute) or manually, both ways call commonInteractive.file_commit Here is the manual way driverCommands.ml "commit", Arg_none (fun o -> List.iter (fun file -> file_commit file ) !!done_files; This code checks every minute if a file was moved to !!done_files and, if auto_commit is set to true, commits the file, this happens in the background driverMain.ml let minute_timer () = if !!auto_commit then List.iter (fun file -> file_commit file ) !!CommonComplexOptions.done_files commonInteractive.file_commit - here the real action of moving files takes place, an important comment from the source code: (******** These two functions 'file_commit' and 'file_cancel' should be the two only functions in mldonkey able to destroy a file, the first one by moving it, the second one by deleting it. Note that when the network specific file_commit function is called, the file has already been moved to the incoming/ directory under its new name. *) - "let incoming" seeks for the appropiate incoming directory, if the downloaded file is a directory (means multifile torrent) another incoming is chosen in respect of the sharing strategy used. The first shared directory with the corresponding strategy is chosen, so there is no need to have several shared directories with a incoming_* strategy. It does however not hurt, files to be shared can be put into those directories. (are directories created if one of the sharing strategies is missing?) - "let new_name" checks for the file name the commit file should have -> file_commited_name commonInteractive.file_commited_name - appends an ongoing number to the filename if the file already exists in incoming - "set_file_disk_name" calls commonFile.set_file_disk_name - commonFile.set_file_disk_name - really moves the files via Unix32.rename - Unix32.rename takes care if the finished file is really a directory from a multifile torrent - Unix32.rename copies the files if renaming fails due to the fact that !!temp_directory and the incoming directory are on seperate physical partitions, this can block the core if the files are huge. It is therefore advised to have !!temp_directory and the incoming directory on the same physical partition - "file_best_name" - take a look at files .ini - one option is called file_filename which is the default name for the file, normally it corresponds with the name present in the ED2K link or the "name" option in a torrent file - another option is called file_filenames, AFAIK only used by the Donkey module here alternative filesnames are stored which is BTW a good way of identifying fake files (result?) - "Unix32.destroy" - MLDonkey has a table of used file descriptors (fd), here the fd of the file is removed - next line removes the empty directory of a multifile torrent in !!temp_directory - "impl.impl_file_ops.op_file_commit impl.impl_file_val new_name" - this long line calls the commit function in the various network modules, keep in mind the file(s) is/are already moved, GGF/FileTP don´t use this function - donkeyInteractive.file_ops.op_file_commit - DonkeyShare.remember_shared_info - unshare_file - bTInteractive.ml - (to be understood;-) and written) - commonShared.new_shared is called - file_state is updated to FileShared - file is removed from !!done_files and !!files (what about the swarmer for the file, they are not removed from files.ini) (what about commonSources, are the sources removed?) ======================================================= Donkey: I noticed that that finished files are present twice in shared_files_new.ini - once with their real name - second with "temp/urn_ed2k_ABABABABABABA" as file name, other data like md4s are the same This seems due to the fact that donkeyShare.remember_shared_info is called twice, once via commonInteractive.file_commit impl.impl_file_ops.op_file_commit impl.impl_file_val new_name donkeyInteractive.file_ops.op_file_commit <- (fun file new_name DonkeyShare.remember_shared_info file new_name and before committing via (to be removed?) donkeyOneFile.declare_completed_file DonkeyShare.remember_shared_info file (file_disk_name file)