Skip to content

Instantly share code, notes, and snippets.

@Wohlstand
Last active March 29, 2023 03:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Wohlstand/cb8f4c9178119fcfba8f1768484e9059 to your computer and use it in GitHub Desktop.
Save Wohlstand/cb8f4c9178119fcfba8f1768484e9059 to your computer and use it in GitHub Desktop.
Copy a huge file with an ability to resume the copying after failure (to rescue it from a dying HDD)
#!/usr/bin/python3
# --------------------------------------------------------------------------------
# MIT License
# Copyright (c) 2023 by Vitaly Novichkov "Wohlstand"
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# --------------------------------------------------------------------------------
# This script was made on a quick hand to rescue data from the dying HDD which
# gets often reset of the connection (possibly because of certain cache chip is glitchy).
# Because of this annoying case, I can't just copy a huge file from the old HDD to new without
# restarting the copying from the scratch. So, I wrote this simple script that copies a file
# by pieces of 100 MB. And so, if source gets a sudden connection reset, script gets errored and stopped.
# So, run the script again, and it will resume at the place where the copying was interrupted.
# After it copies the whole file data, then it builds one united file from the ton of fragments to
# finalise the work.
import os
# Source file path to copy
src = "/media/mint/VMs/Windows/Win7/Win-7.vdi"
# Destinition file path
dst = "/media/mint/VMs-NEW/Windows/Win7/Win-7.vdi"
# Temp directory to store fragments (must be empty)
dst_d = "/media/mint/VMs-NEW/Windows/Win7/_tmp/"
src_stats = os.stat(src)
f = open(src, "rb")
chunk_size = 104857600 # 100 MB
count = 0
total_bytes = 0
src_size = src_stats.st_size
possible_total = src_size / chunk_size
rrr = True
print("Копирование файла %s" % src)
while rrr:
dst_f = "%sxxx-%d.bin" % (dst_d, count)
if os.path.exists(dst_f):
chunk_stats = os.stat(dst_f)
print("Фрагмент %d/%d существует, пропускаем %d байт" % (count, possible_total, chunk_stats.st_size))
count += 1
f.seek(chunk_stats.st_size, 1)
total_bytes += chunk_stats.st_size
continue
chunk = f.read(chunk_size)
if len(chunk) < chunk_size:
print("Блок меньше, возможно конец файла")
rrr = False
total_bytes += len(chunk)
df = open(dst_f, "wb")
df.write(chunk);
df.close();
print("Фрагмент %d/%d весом %d (всего скопировано %d байт, %g ГБ, %g ГиБ)" % (count, possible_total, len(chunk), total_bytes, total_bytes / 1000000000, total_bytes / (1024*1024*1024)))
count += 1
f.close()
print("Данные готовы, вес %g Гб или %g ГиБ" % (total_bytes / 1000000000, total_bytes / (1024*1024*1024)))
# Склеиваем фрагменты в единый файл
print("Склеивание файла %s" % dst)
rrr = True
count = 0
total_bytes_prev = total_bytes
total_bytes = 0
f = open(dst, "wb")
while rrr:
dst_f = "%sxxx-%d.bin" % (dst_d, count)
if not os.path.exists(dst_f):
print("Фрагменты кончились, файл готов!")
break
df = open(dst_f, "rb")
chunk = df.read();
df.close();
os.unlink(dst_f)
f.write(chunk);
total_bytes += len(chunk)
print("Фрагмент %d/%d весом %d (всего записано %d байт, %g ГБ, %g ГиБ)" % (count, possible_total, len(chunk), total_bytes, total_bytes / 1000000000, total_bytes / (1024*1024*1024)))
count += 1
f.close();
print("Данные готовы, вес %g Гб или %g ГиБ (пред %g Гб или %g ГиБ)" % (total_bytes / 1000000000, total_bytes / (1024*1024*1024), total_bytes_prev / 1000000000, total_bytes_prev / (1024*1024*1024)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment