changeset 1748:b8d37cc79410

copy_file(): remove incomplete file on error (bug 2890715) The behavior changed, data is first written to a temporary file, which is unlinked in case of error, then the tempfile is renamed to the final name. Size of buffer was increased from 4k to 16k.
author zas_
date Sun, 10 Jan 2010 14:23:29 +0000
parents 10f9803aca45
children ec9a399b03d5
files src/ui_fileops.c
diffstat 1 files changed, 40 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/src/ui_fileops.c	Sat Jan 09 11:44:27 2010 +0000
+++ b/src/ui_fileops.c	Sun Jan 10 14:23:29 2010 +0000
@@ -520,52 +520,66 @@
 {
 	FILE *fi = NULL;
 	FILE *fo = NULL;
-	gchar *sl, *tl;
-	gchar buf[4096];
+	gchar *sl = NULL;
+	gchar *tl = NULL;
+	gchar *randname = NULL;
+	gchar buf[16384];
 	size_t b;
+	gint ret = FALSE;
+	gint fd = -1;
 
 	sl = path_from_utf8(s);
 	tl = path_from_utf8(t);
 
 	if (hard_linked(sl, tl))
 		{
-		g_free(sl);
-		g_free(tl);
-		return TRUE;
+		ret = TRUE;
+		goto end;
 		}
 
 	fi = fopen(sl, "rb");
-	if (fi)
-		{
-		fo = fopen(tl, "wb");
-		if (!fo)
-			{
-			fclose(fi);
-			fi = NULL;
-			}
-		}
-
-	g_free(sl);
-	g_free(tl);
-
-	if (!fi || !fo) return FALSE;
+	if (!fi) goto end;
+	
+	/* First we write to a temporary file, then we rename it on success,
+	   and attributes from original file are copied */
+	randname = g_strconcat(tl, ".tmp_XXXXXX", NULL);
+	if (!randname) goto end;
+	
+	fd = g_mkstemp(randname);
+	if (fd == -1) goto end;
+	
+	fo = fdopen(fd, "wb");
+	if (!fo) {
+		close(fd);
+		goto end;
+	}
 
 	while ((b = fread(buf, sizeof(gchar), sizeof(buf), fi)) && b != 0)
 		{
 		if (fwrite(buf, sizeof(gchar), b, fo) != b)
 			{
-			fclose(fi);
-			fclose(fo);
-			return FALSE;
+			unlink(randname);
+			goto end;
 			}
 		}
 
-	fclose(fi);
-	fclose(fo);
+	fclose(fi); fi = NULL;
+	fclose(fo); fo = NULL;
+
+	if (rename(randname, tl) < 0) {
+		unlink(randname);
+		goto end; 	
+	}
 
-	copy_file_attributes(s, t, TRUE, TRUE);
+	ret = copy_file_attributes(s, t, TRUE, TRUE);
 
-	return TRUE;
+end:
+	if (fi) fclose(fi);
+	if (fo) fclose(fo);
+	if (sl) g_free(sl);
+	if (tl) g_free(tl);
+	if (randname) g_free(randname);
+	return ret;
 }
 
 gboolean move_file(const gchar *s, const gchar *t)