Generate the MD5 checksum of the specified file.


    @param file_path_name: the absolute path and name of the file to
        generate its MD5 checksum.

    @param hash_algorithm_name: specify the hash algorithm to use.  Refer
        to ``hashlib.algorithms`` to get the names of the hash algorithms
        guaranteed to be supported by this module.


    @return: hash digest returned as a string of double length, containing
        only hexadecimal digits.  This may be used to exchange the value
        safely in email or other non-binary environments.


    @note: the file can not be entirely read to memory, but it needs to
        be read by chunks of memory that will be freed after each
        iteration.  What's important to notice is that the file  must be
        opened in binary mode.  The function breaks the file into chunks
        using block size of any multiple of 128 (say 8192, 32768, etc.)
        and its feed them to MD5 consecutively using ``update()``.  This
        takes advantage advantage of the fact that MD5 has 128-byte digest
        blocks.  The function actually uses a block size that depends on the block
        size of the filesystem to avoid performances issues.

    @note: The ``F_FRSIZE`` value is the actual minimum allocation unit
        of the filesystem, while the ``F_BSIZE`` is the block size that
        would lead to most efficient use of the disk with io calls.

def get_file_checksum(file_path_name,
        hash_algorithm_name='md5'):
    """
    Generate the MD5 checksum of the specified file.


    @param file_path_name: the absolute path and name of the file to
        generate its MD5 checksum.

    @param hash_algorithm_name: specify the hash algorithm to use.  Refer
        to ``hashlib.algorithms`` to get the names of the hash algorithms
        guaranteed to be supported by this module.


    @return: hash digest returned as a string of double length, containing
        only hexadecimal digits.  This may be used to exchange the value
        safely in email or other non-binary environments.


    @note: the file can not be entirely read to memory, but it needs to
        be read by chunks of memory that will be freed after each
        iteration.  What's important to notice is that the file  must be
        opened in binary mode.  The function breaks the file into chunks
        using block size of any multiple of 128 (say 8192, 32768, etc.)
        and its feed them to MD5 consecutively using ``update()``.  This
        takes advantage advantage of the fact that MD5 has 128-byte digest
        blocks.  The function actually uses a block size that depends on the block
        size of the filesystem to avoid performances issues.

    @note: The ``F_FRSIZE`` value is the actual minimum allocation unit
        of the filesystem, while the ``F_BSIZE`` is the block size that
        would lead to most efficient use of the disk with io calls.
    """
    hash_algorithm = hashlib.new(hash_algorithm_name)

    if sys.platform == "win32":
        import ctypes
        sectors_per_cluster = ctypes.c_ulonglong(0)
        bytes_per_sector = ctypes.c_ulonglong(0)
        root_path_name = ctypes.c_wchar_p(u"C:\\")
        ctypes.windll.kernel32.GetDiskFreeSpaceW(root_path_name,
                ctypes.pointer(sectors_per_cluster),
                ctypes.pointer(bytes_per_sector),
                None,
                None)
        block_size = bytes_per_sector.value
    else:
        import statvfs
        block_size = os.statvfs('/')[statvfs.F_BSIZE]

    with open(file_path_name, 'rb') as handle:
        for chunk in iter(lambda: handle.read(block_size), b''):
            hash_algorithm.update(chunk)

    return hash_algorithm.hexdigest()