From MLDonkey
Jump to: navigation, search
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?

A timer is created in 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

- 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

- calls donkeyOneFile.declare_completed_file

- among other things most important calls 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

- puts the file into hashtable dirnames
- iters through all network modules and calls CommonNetwork.network_share

- calls op_network_share in all network modules
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
This is a bit confusing and should be changed in the future, shared_files_new.ini
is maintained only by, shared_files.ini is maintained by

Here update_shared_num is called which is again in commonShared:
- 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:
     "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 It therefore can
not be accessed directly from, so shared_iter is used for this.

The next command in the timer in 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


Another timer is created in
   add_session_timer enabler 60.0 (fun timer ->
        BTClients.recover_files ();

bTClients.recover_files ()
- calls 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

- notifies the tracker that the file is finished
- calls commonInteractive.file_completed (see notes in the Donkey section)

- 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
   "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
  let minute_timer () =
  if !!auto_commit then
    List.iter (fun file ->
        file_commit file
    ) !!CommonComplexOptions.done_files

- 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
- "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
  - 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
    - 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
- "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
    - (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
is called twice, once via

  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?)
    DonkeyShare.remember_shared_info file (file_disk_name file)
Personal tools