Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflink(Copy On Write)支持 #39

Merged
merged 9 commits into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ mcd_root/

是否在备份时临时关闭自动保存

### copy_on_write

默认值: `false`

使用某些文件系统的写时拷贝(一种增量备份)

### ignored_files

默认值:
Expand Down
6 changes: 6 additions & 0 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ Default: `true`

If turn off auto save when making backup or not

### copy_on_write

Default: `false`

Useing copy_on_write in some File system(incremental backup)

### ignored_files

If ignore file `session.lock` during backup, which can
Expand Down
28 changes: 26 additions & 2 deletions quick_backup_multi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,30 @@ def get_backup_file_name(backup_format: BackupFormat):
raise ValueError('unknown backup mode {}'.format(backup_format))


copy_file_range_supported=hasattr(os, "copy_file_range")
COW_COPY_BUFFER_SIZE = 2**30 # 1GB / need int, may overflow, so cannot copy files larger than 2GB in a single pass

#copy using "Copy On Write"
def _cpcow(src_path: str, dst_path: str):
if not copy_file_range_supported or not config.copy_on_write:
return shutil.copy2(src_path, dst_path)

if os.path.isdir(dst_path):
dst_path = os.path.join(dst_path, os.path.basename(src_path))

try:
with open(src_path,'rb') as fsrc, open(dst_path,'wb+') as fdst:
while os.copy_file_range(fsrc.fileno(), fdst.fileno(), COW_COPY_BUFFER_SIZE):
pass

except Exception as e:
server_inst.logger.warning(str(e) + str(src_path) + "->" + str(dst_path) + ",Retry with other functions")
shutil.copy(src_path, dst_path)

shutil.copystat(src_path, dst_path) # copy2
return dst_path


def copy_worlds(src: str, dst: str, intent: CopyWorldIntent, *, backup_format: Optional[BackupFormat] = None):
if backup_format is None:
backup_format = get_backup_format()
Expand All @@ -96,12 +120,12 @@ def copy_worlds(src: str, dst: str, intent: CopyWorldIntent, *, backup_format: O

server_inst.logger.info('copying {} -> {}'.format(src_path, dst_path))
if os.path.isdir(src_path):
shutil.copytree(src_path, dst_path, ignore=lambda path, files: set(filter(config.is_file_ignored, files)))
shutil.copytree(src_path, dst_path, ignore=lambda path, files: set(filter(config.is_file_ignored, files)), copy_function=_cpcow)
elif os.path.isfile(src_path):
dst_dir = os.path.dirname(dst_path)
if not os.path.isdir(dst_dir):
os.makedirs(dst_dir)
shutil.copy(src_path, dst_path)
_cpcow(src_path, dst_path)
else:
server_inst.logger.warning('{} does not exist while copying ({} -> {})'.format(src_path, src_path, dst_path))
elif backup_format == BackupFormat.tar or backup_format == BackupFormat.tar_gz:
Expand Down
1 change: 1 addition & 0 deletions quick_backup_multi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class SlotInfo(Serializable):
class Configuration(Serializable):
size_display: bool = True
turn_off_auto_save: bool = True
copy_on_write: bool = False
ignored_files: List[str] = [
'session.lock'
]
Expand Down