def _generate_compressed_dir_changes(self, dir_changes): dir_changes = copy.deepcopy(dir_changes) for c in dir_changes.flat_changes(): if c.cur_info and c.cur_info.tmp_file: compressed_tmp_filename = change_entry.generate_tmp_file(self.tmp_dir) compressed_tmp_file = os.path.join(self.tmp_dir, compressed_tmp_filename) tmp_file_with_realname = os.path.join(self.tmp_dir, os.path.basename(c.path)) # Rename the tmp file so that the archive includes the original # filename. os.rename(c.cur_info.tmp_file, tmp_file_with_realname) compression.compress_file(tmp_file_with_realname, compressed_tmp_file, password=self.password, encryption_method=self.encryption_method, compression_level=self.compression_level) # Rename the tmp filename back. os.rename(tmp_file_with_realname, c.cur_info.tmp_file) tmp_fi = file_info.load_file_info(compressed_tmp_file) compressed_file_info = file_info.FileInfo( # TODO: check conflict of compressed filename? compression.get_compressed_filename(c.cur_info.path), c.cur_info.is_dir, c.cur_info.mode, tmp_fi.size, c.cur_info.last_modified_time) c.cur_info.compressed_file_info = file_info.copy_with_tmp_file( compressed_file_info, compressed_tmp_filename, self.tmp_dir) return dir_changes
def _get_file_conflict_state(change, full_path, force_conflict): if os.path.isdir(full_path): return CONFLICT_NEW elif force_conflict: return force_conflict elif os.path.isfile(full_path): fi = file_info.load_file_info(full_path) if ((change.old_info and not change.old_info.is_modified(fi)) or (change.cur_info and not change.cur_info.is_modified(fi)) or (change.conflict_info and not change.conflict_info.is_modified(fi))): return CONFLICT_NO_CONFLICT else: # Keep the dest unchanged, because it may be opened return CONFLICT_NEW else: # No exist return CONFLICT_NO_CONFLICT
def _generate_original_dir_changes(self, dir_changes, parent_dir_changes=None, parent_invalid_archive_dc_working=None, parent_invalid_archive_dc_cloud=None): invalid_archive_dc_working = change_entry.DirChanges( dir_changes.base_dir(), change_entry.CONTENT_STATUS_MODIFIED, parent_dir_changes=parent_invalid_archive_dc_working) invalid_archive_dc_cloud = change_entry.DirChanges( dir_changes.base_dir(), change_entry.CONTENT_STATUS_MODIFIED, parent_dir_changes=parent_invalid_archive_dc_cloud) new_dir_changes = change_entry.DirChanges( dir_changes.base_dir(), dir_changes.dir_status(), parent_dir_changes=parent_dir_changes) for c in dir_changes.changes(): path = compression.get_original_filename(c.path) old_info = None if c.old_info and c.old_info.original_file_info: old_info = copy.deepcopy(c.old_info.original_file_info) old_info.compressed_file_info = copy.deepcopy(c.old_info) old_info.compressed_file_info.original_file_info = None else: old_info = copy.deepcopy(c.old_info) cur_info = None if c.cur_info: if c.cur_info.is_dir: cur_info = c.cur_info elif c.cur_info.tmp_file: original_tmp_filename = change_entry.generate_tmp_file(self.tmp_dir) original_tmp_file = os.path.join(self.tmp_dir, original_tmp_filename) if not compression.is_compressed_filename(c.cur_info.path): invalid_archive_dc_working.add_change(change_entry.ChangeEntry( path, c.cur_info, c.old_info, c.content_status, parent_dir_changes=invalid_archive_dc_working)) invalid_archive_dc_cloud.add_change(change_entry.ChangeEntry( path, None, c.cur_info, # Remove the file directly because we move it to working dir change_entry.CONTENT_STATUS_DELETED, parent_dir_changes=invalid_archive_dc_cloud)) continue try: compression.decompress_file(c.cur_info.tmp_file, original_tmp_file, self.tmp_dir, password=self.password) except compression.CompressionInvalidArchive: invalid_archive_dc_working.add_change(change_entry.ChangeEntry( # Not using path because it is not a valid archive c.path, c.cur_info, c.old_info, c.content_status, parent_dir_changes=invalid_archive_dc_working)) invalid_archive_dc_cloud.add_change(change_entry.ChangeEntry( # Not using path because it is not a valid archive c.path, None, c.cur_info, # Remove the file directly because we move it to working dir change_entry.CONTENT_STATUS_DELETED, parent_dir_changes=invalid_archive_dc_cloud)) continue tmp_fi = file_info.load_file_info(original_tmp_file) compressed_file_info = copy.deepcopy(c.cur_info) compressed_file_info.compressed_file_info = None compressed_file_info.original_file_info = None cur_info = file_info.FileInfo( # TODO: check conflict of original filename? compression.get_original_filename(c.cur_info.path), c.cur_info.is_dir, c.cur_info.mode, tmp_fi.size, c.cur_info.last_modified_time, compressed_file_info=compressed_file_info) cur_info = file_info.copy_with_tmp_file( cur_info, original_tmp_filename, self.tmp_dir) else: cur_info = old_info sub_dir_changes = None sub_invalid_archive_dc_working = None if c.dir_changes: result = self._generate_original_dir_changes( c.dir_changes, parent_dir_changes=new_dir_changes, parent_invalid_archive_dc_working=invalid_archive_dc_working) sub_dir_changes = result[0] sub_invalid_archive_dc_working = result[1] sub_invalid_archive_dc_cloud = result[2] if sub_invalid_archive_dc_working: invalid_archive_dc_working.add_change(change_entry.ChangeEntry( path, cur_info, old_info, c.content_status, dir_changes=sub_invalid_archive_dc_working, parent_dir_changes=invalid_archive_dc_working)) if sub_invalid_archive_dc_cloud: invalid_archive_dc_cloud.add_change(change_entry.ChangeEntry( path, cur_info, old_info, c.content_status, dir_changes=sub_invalid_archive_dc_cloud, parent_dir_changes=invalid_archive_dc_cloud)) new_dir_changes.add_change(change_entry.ChangeEntry( path, cur_info, old_info, c.content_status, dir_changes=sub_dir_changes, parent_dir_changes=new_dir_changes)) if not invalid_archive_dc_working.changes(): invalid_archive_dc_working = None if not invalid_archive_dc_cloud.changes(): invalid_archive_dc_cloud = None return new_dir_changes, invalid_archive_dc_working, invalid_archive_dc_cloud
def apply_dir_changes_to_dir(dest_dir, dir_changes, force_conflict=None, verbose=False): for c in dir_changes.changes(): full_path = os.path.realpath(os.path.join(dest_dir, c.path)) if c.content_status == CONTENT_STATUS_TO_FILE: if not os.path.exists(full_path): if verbose: print 'Create file %s' % full_path c.cur_info.copy_tmp(full_path) elif os.path.isdir(full_path): apply_dir_changes_to_dir(dest_dir, c.dir_changes, force_conflict=force_conflict, verbose=verbose) if os.listdir(full_path): # Conflict, the directory still exists if verbose: print 'Create conflict file %s' % _get_conflict_copy_path(full_path) c.cur_info.copy_tmp(_get_conflict_copy_path(full_path)) else: if verbose: print 'Delete dir %s' % full_path print 'Create file %s' % full_path os.rmdir(full_path) c.cur_info.copy_tmp(full_path) else: # full_path is a file if c.cur_info.is_modified(file_info.load_file_info(full_path)): # Conflict, the existing file is changed if verbose: print 'Create conflict file %s' % _get_conflict_copy_path(full_path) c.cur_info.copy_tmp(_get_conflict_copy_path(full_path)) elif c.content_status == CONTENT_STATUS_TO_DIR: if not os.path.exists(full_path): if verbose: print 'Create dir %s' % full_path os.mkdir(full_path) apply_dir_changes_to_dir(dest_dir, c.dir_changes, force_conflict=force_conflict, verbose=verbose) elif os.path.isdir(full_path): apply_dir_changes_to_dir(dest_dir, c.dir_changes, force_conflict=force_conflict, verbose=verbose) else: # full_path is still a file if c.old_info.is_modified(file_info.load_file_info(full_path)): if verbose: print 'Move to conflict file %s' % _get_conflict_copy_path( full_path) # Conflict, the existing file is changed os.rename(full_path, _get_conflict_copy_path(full_path)) else: if verbose: print 'Delete file %s' % full_path os.remove(full_path) if verbose: print 'Create dir %s' % full_path os.mkdir(full_path) apply_dir_changes_to_dir(dest_dir, c.dir_changes, force_conflict=force_conflict, verbose=verbose) elif ((not c.cur_info or not c.cur_info.is_dir) and (not c.old_info or not c.old_info.is_dir)): # File change if c.content_status in [CONTENT_STATUS_NEW, CONTENT_STATUS_MODIFIED]: conflict_state = _get_file_conflict_state(c, full_path, force_conflict) if conflict_state == CONFLICT_NO_CONFLICT: if verbose: if c.content_status == CONTENT_STATUS_NEW: print 'Create file %s' % full_path else: print 'Update file %s' % full_path c.cur_info.copy_tmp(full_path) elif conflict_state == CONFLICT_NEW: if verbose: print 'Create conflict file %s' % _get_conflict_copy_path(full_path) c.cur_info.copy_tmp(_get_conflict_copy_path(full_path)) elif conflict_state == CONFLICT_DEST: if verbose: print 'Move to conflict file %s' % _get_conflict_copy_path(full_path) print 'Create file %s' % full_path shutil.move(full_path, _get_conflict_copy_path(full_path)) c.cur_info.copy_tmp(full_path) elif c.content_status == CONTENT_STATUS_DELETED: conflict_state = _get_file_conflict_state(c, full_path, force_conflict) if conflict_state == CONFLICT_NO_CONFLICT: if os.path.exists(full_path): if verbose: print 'Delete file %s' % full_path os.remove(full_path) else: # Dir change if c.content_status in [CONTENT_STATUS_NEW, CONTENT_STATUS_MODIFIED]: conflict_state = _get_dir_conflict_state(c, full_path) if conflict_state == CONFLICT_NO_CONFLICT: if not os.path.exists(full_path): if verbose: print 'Create dir %s' % full_path os.mkdir(full_path) elif conflict_state == CONFLICT_DEST: if verbose: print 'Move to conflict file %s' % _get_conflict_copy_path(full_path) print 'Create dir %s' % full_path shutil.move(full_path, _get_conflict_copy_path(full_path)) os.mkdir(full_path) apply_dir_changes_to_dir(dest_dir, c.dir_changes, force_conflict=force_conflict, verbose=verbose) elif c.content_status == CONTENT_STATUS_DELETED: conflict_state = _get_dir_conflict_state(c, full_path) if conflict_state == CONFLICT_NO_CONFLICT: apply_dir_changes_to_dir(dest_dir, c.dir_changes, force_conflict=force_conflict, verbose=verbose) if os.path.isdir(full_path) and not os.listdir(full_path): if verbose: print 'Delete dir %s' % full_path os.rmdir(full_path) else: # No change apply_dir_changes_to_dir(dest_dir, c.dir_changes, force_conflict=force_conflict, verbose=verbose)