def rmdirRecursiveWindows(dir): """Windows-specific version of rmdirRecursive that handles path lengths longer than MAX_PATH. """ dir = os.path.realpath(dir) # Make sure directory is writable SetFileAttributesW('\\\\?\\' + dir, FILE_ATTRIBUTE_NORMAL) for ffrec in FindFiles('\\\\?\\' + dir + '\\*.*'): file_attr = ffrec[0] name = ffrec[8] if name == '.' or name == '..': continue full_name = os.path.join(dir, name) if file_attr & FILE_ATTRIBUTE_DIRECTORY: rmdirRecursiveWindows(full_name) else: SetFileAttributesW('\\\\?\\' + full_name, FILE_ATTRIBUTE_NORMAL) DeleteFile('\\\\?\\' + full_name) RemoveDirectory('\\\\?\\' + dir)
def file_wipe(file_name): # add \\?\ if it does not exist to support Unicode and long paths file_name = extended_path(file_name) check_os() win_version, _ = determine_win_version() volume = volume_from_file(file_name) volume_info = get_volume_information(volume) cluster_size = (volume_info.sectors_per_cluster * volume_info.bytes_per_sector) file_handle = open_file(file_name) file_size, is_special = get_file_basic_info(file_name, file_handle) orig_extents = get_extents(file_handle) if is_special: bridged_extents = [ x for x in logical_ranges_to_extents( get_extents(file_handle, False), True) ] CloseHandle(file_handle) #logger.debug('Original extents: {}'.format(orig_extents)) volume_handle = obtain_readwrite(volume) attrs = GetFileAttributesW(file_name) if attrs & FILE_ATTRIBUTE_READONLY: # Remove read-only attribute to avoid "access denied" in CreateFileW(). SetFileAttributesW(file_name, attrs & ~FILE_ATTRIBUTE_READONLY) file_handle = open_file(file_name, GENERIC_READ | GENERIC_WRITE) if not is_special: # Direct overwrite when it's a regular file. #logger.info("Attempting direct file wipe.") wipe_file_direct(file_handle, orig_extents, cluster_size, file_size) new_extents = get_extents(file_handle) CloseHandle(file_handle) #logger.debug('New extents: {}'.format(new_extents)) if orig_extents == new_extents: clean_up(None, volume_handle, None) return # Expectation was that extents should be identical and file is wiped. # If OS didn't give that to us, continue below and use defrag wipe. # Any extent within new_extents has now been wiped by above. # It can be subtracted from the orig_extents list, and now we will # just clean up anything not yet overwritten. orig_extents = extents_a_minus_b(orig_extents, new_extents) else: # File needs special treatment. We can't just do a basic overwrite. # First we will truncate it. Then chase down the freed clusters to # wipe them, now that they are no longer part of the file. truncate_file(file_handle) CloseHandle(file_handle) # Poll to confirm that our clusters were freed. poll_clusters_freed(volume_handle, volume_info.total_clusters, orig_extents) # Chase down all the freed clusters we can, and wipe them. #logger.debug("Attempting defrag file wipe.") # Put the temp file in the same folder as the target wipe file. # Should be able to write this path if user can write the wipe file. tmp_file_path = os.path.dirname(file_name) + os.sep + tmp_file_name if is_special: orig_extents = choose_if_bridged(volume_handle, volume_info.total_clusters, orig_extents, bridged_extents) for lcn_start, lcn_end in orig_extents: result = wipe_extent_by_defrag(volume_handle, lcn_start, lcn_end, cluster_size, volume_info.total_clusters, tmp_file_path) # Clean up. clean_up(None, volume_handle, tmp_file_path) return