From 856c29184e1c590ddc3ffecb6cc3065982230471 Mon Sep 17 00:00:00 2001 From: usmannasir Date: Sun, 26 Oct 2025 15:18:26 +0500 Subject: [PATCH] Fix replace-file API to use temp file approach instead of here-document - Write content to /home/cyberpanel/scanner_temp_{scan_id}_{timestamp}.tmp first - Use Python open() instead of shell here-document to avoid: - lscpd connection reset for large files (>2KB) - EOF_MARKER conflicts in file content - Shell command size limits - Copy temp file to user directory using executioner with user context - Clean up CyberPanel temp file after copy - Fixes: [Errno 104] Connection reset by peer when writing large files --- aiScanner/api.py | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/aiScanner/api.py b/aiScanner/api.py index 20c843f35..821a1ee6d 100644 --- a/aiScanner/api.py +++ b/aiScanner/api.py @@ -1254,15 +1254,37 @@ def scanner_replace_file(request): return JsonResponse({'success': False, 'error': 'Failed to create backup', 'error_code': 'BACKUP_FAILED'}, status=500) # Write new content to temp file first (atomic write) - temp_path = f'{full_path}.tmp.{int(time.time())}' + # Write to /home/cyberpanel first (where CyberPanel has write access) + cyberpanel_temp = f'/home/cyberpanel/scanner_temp_{scan_id}_{int(time.time())}.tmp' + user_temp_path = f'{full_path}.tmp.{int(time.time())}' - # Write content using a here-document to avoid shell escaping issues - write_cmd = f'cat > "{temp_path}" << \'EOF_MARKER\'\n{content}\nEOF_MARKER' - write_result = ProcessUtilities.executioner(write_cmd, user=user) + try: + # Write content directly to CyberPanel temp directory + with open(cyberpanel_temp, 'w', encoding='utf-8') as f: + f.write(content) + logging.writeToFile(f'[API] Wrote {len(content)} bytes to {cyberpanel_temp}') + except Exception as e: + error_msg = f'Failed to write temp file: {str(e)}' + logging.writeToFile(f'[API] {error_msg}') + log_file_operation(scan_id, 'replace', file_path, False, error_msg, backup_path=backup_path, request=request) + return JsonResponse({'success': False, 'error': 'Failed to write file', 'error_code': 'WRITE_FAILED'}, status=500) + + # Copy temp file to user directory with correct ownership + cp_cmd = f'cp "{cyberpanel_temp}" "{user_temp_path}"' + cp_result = ProcessUtilities.executioner(cp_cmd, user=user) + + # Clean up CyberPanel temp file + try: + import os + os.remove(cyberpanel_temp) + except: + pass # executioner returns 1 for success, 0 for failure - if write_result != 1: - log_file_operation(scan_id, 'replace', file_path, False, 'Failed to write temp file', backup_path=backup_path, request=request) + if cp_result != 1: + error_msg = 'Failed to copy temp file to user directory' + logging.writeToFile(f'[API] {error_msg}') + log_file_operation(scan_id, 'replace', file_path, False, error_msg, backup_path=backup_path, request=request) return JsonResponse({'success': False, 'error': 'Failed to write file', 'error_code': 'WRITE_FAILED'}, status=500) # Get original file permissions @@ -1273,17 +1295,17 @@ def scanner_replace_file(request): permissions = stat_result[1].strip() # Set permissions on temp file - chmod_cmd = f'chmod {permissions} "{temp_path}"' + chmod_cmd = f'chmod {permissions} "{user_temp_path}"' ProcessUtilities.executioner(chmod_cmd, user=user) # Atomic rename - mv_cmd = f'mv "{temp_path}" "{full_path}"' + mv_cmd = f'mv "{user_temp_path}" "{full_path}"' mv_result = ProcessUtilities.executioner(mv_cmd, user=user) # executioner returns 1 for success, 0 for failure if mv_result != 1: # Cleanup temp file - ProcessUtilities.executioner(f'rm -f "{temp_path}"', user=user) + ProcessUtilities.executioner(f'rm -f "{user_temp_path}"', user=user) log_file_operation(scan_id, 'replace', file_path, False, 'Failed to replace file', backup_path=backup_path, request=request) return JsonResponse({'success': False, 'error': 'Failed to replace file', 'error_code': 'REPLACE_FAILED'}, status=500)