Hi Gary!

On Mo, 14 Mai 2012, Gary Johnson wrote:

> On 2012-05-14, Christian Brabandt wrote:
> 
> > This patch fixes a data conversion error, that leads to a data loss. 
> > Problem is, that open() is done, even though it is not clear, conversion 
> > will work. So this patch runs buf_write_bytes() twice, first time is to 
> > check that each character can be converted successfully and only if this 
> > does not return, does it really open the file and write the result.
> > 
> > Problem is, this will probably slow down writing speed, as 
> > buf_write_bytes needs to be called twice.
> > 
> > I have tested it with the given sample and it works here.
> 
> How much slower is it for a file large enough that the write times
> are more than a second?
> 
> I don't think we want to noticeably slow down Vim for everyone all
> the time in order to more-conveniently avoid an error that is
> observed once in 15 years.

A small test suggests, that for a 15M file, writing happens here within 
0,44s while the patched version takes 1,08s. For smaller files, the time 
is negligible, but I fear, for larger files, it is quite noticeable.

Here is another test with a 40B, 1K, 4K, 50K, 500K, 1M, 15M, 70M files:

Size    Run     Patched Non-patched
40B     1       0,096s  0,089s
        2       0,084s  0,114s
        3       0,108s  0,099s
        Ø       0,096s  0,101s
1K      1       0,085s  0,080s
        2       0,108s  0,079s
        3       0,107s  0,099s
        Ø       0,100s  0,086s
4K      1       0,096s  0,072s
        2       0,096s  0,089s
        3       0,087s  0,089s
        Ø       0,093s  0,083s
50K     1       0,117s  0,082s
        2       0,148s  0,114s
        3       0,164s  0,088s
        Ø       0,143s  0,095s
100K    1       0,138s  0,102s
        2       0,160s  0,079s
        3       0,137s  0,090s
        Ø       0,145s  0,090s
500K    1       0,159s  0,102s
        2       0,185s  0,083s
        3       0,186s  0,113s
        Ø       0,177s  0,099s
1M      1       0,216s  0,089s
        2       0,233s  0,108s
        3       0,206s  0,113s
        Ø       0,218s  0,103s
15M     1       1,098s  0,212s
        2       1,089s  0,110s
        3       1,081s  0,097s
        Ø       1,089s  0,140s
70M     1       6,809s  0,097s
        2       7,160s  0,193s
        3       0,699s  0,097s
        Ø       4,889s  0,129s

Here the non-patched versions are a lot faster, because the bug is 
triggered at the beginning of the filewrite, and writing is aborted 
afterwards, While the patched version try twice.

Here is a small update to the patch: break in case of errors.
But perhaps, it isn't worth the trouble, since 'writebackup' prevents 
that bug.

regards,
Christian

-- 
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
diff --git a/src/fileio.c b/src/fileio.c
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -113,6 +113,7 @@
     int		bw_fd;		/* file descriptor */
     char_u	*bw_buf;	/* buffer with data to be written */
     int		bw_len;		/* length of data */
+    int         do_write;       /* really write */
 #ifdef HAS_BW_FLAGS
     int		bw_flags;	/* FIO_ flags */
 #endif
@@ -3163,6 +3164,7 @@
     int		    prev_got_int = got_int;
     int		    file_readonly = FALSE;  /* overwritten file is read-only */
     static char	    *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')";
+    int		    check_conv;		    /* nr of loops */
 #if defined(UNIX) || defined(__EMX__XX)	    /*XXX fix me sometime? */
     int		    made_writable = FALSE;  /* 'w' bit has been set */
 #endif
@@ -4336,423 +4338,453 @@
 	notconverted = TRUE;
     }
 #endif
-
+#ifdef FEAT_MBYTE
+    check_conv = !converted;
+#else
+    check_conv = TRUE;
+#endif
     /*
-     * Open the file "wfname" for writing.
-     * We may try to open the file twice: If we can't write to the
-     * file and forceit is TRUE we delete the existing file and try to create
-     * a new one. If this still fails we may have lost the original file!
-     * (this may happen when the user reached his quotum for number of files).
-     * Appending will fail if the file does not exist and forceit is FALSE.
+     * Loop twice, first time check, that conversion can be done successfully
+     * so in case of an error one does not truncate the file.
      */
-    while ((fd = mch_open((char *)wfname, O_WRONLY | O_EXTRA | (append
-			? (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
-			: (O_CREAT | O_TRUNC))
-			, perm < 0 ? 0666 : (perm & 0777))) < 0)
-    {
-	/*
-	 * A forced write will try to create a new file if the old one is
-	 * still readonly. This may also happen when the directory is
-	 * read-only. In that case the mch_remove() will fail.
-	 */
-	if (errmsg == NULL)
-	{
-#ifdef UNIX
-	    struct stat	st;
-
-	    /* Don't delete the file when it's a hard or symbolic link. */
-	    if ((!newfile && st_old.st_nlink > 1)
-		    || (mch_lstat((char *)fname, &st) == 0
-			&& (st.st_dev != st_old.st_dev
-			    || st.st_ino != st_old.st_ino)))
-		errmsg = (char_u *)_("E166: Can't open linked file for writing");
-	    else
-#endif
-	    {
-		errmsg = (char_u *)_("E212: Can't open file for writing");
-		if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
-								 && perm >= 0)
-		{
-#ifdef UNIX
-		    /* we write to the file, thus it should be marked
-		       writable after all */
-		    if (!(perm & 0200))
-			made_writable = TRUE;
-		    perm |= 0200;
-		    if (st_old.st_uid != getuid() || st_old.st_gid != getgid())
-			perm &= 0777;
-#endif
-		    if (!append)	    /* don't remove when appending */
-			mch_remove(wfname);
-		    continue;
-		}
-	    }
-	}
-
-restore_backup:
-	{
-	    struct stat st;
-
+    for (; check_conv < 2 && end > 0; check_conv++)
+    {
+	if (check_conv)
+	    write_info.do_write = TRUE;
+	else
+	    write_info.do_write = FALSE;
+
+	if (check_conv)
+	{
 	    /*
-	     * If we failed to open the file, we don't need a backup. Throw it
-	     * away.  If we moved or removed the original file try to put the
-	     * backup in its place.
-	     */
-	    if (backup != NULL && wfname == fname)
-	    {
-		if (backup_copy)
+	    * Open the file "wfname" for writing.
+	    * We may try to open the file twice: If we can't write to the
+	    * file and forceit is TRUE we delete the existing file and try to create
+	    * a new one. If this still fails we may have lost the original file!
+	    * (this may happen when the user reached his quotum for number of files).
+	    * Appending will fail if the file does not exist and forceit is FALSE.
+	    */
+		while ((fd = mch_open((char *)wfname, O_WRONLY | O_EXTRA | (append
+				    ? (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
+				    : (O_CREAT | O_TRUNC))
+				    , perm < 0 ? 0666 : (perm & 0777))) < 0)
 		{
 		    /*
-		     * There is a small chance that we removed the original,
-		     * try to move the copy in its place.
-		     * This may not work if the vim_rename() fails.
-		     * In that case we leave the copy around.
-		     */
-		    /* If file does not exist, put the copy in its place */
-		    if (mch_stat((char *)fname, &st) < 0)
-			vim_rename(backup, fname);
-		    /* if original file does exist throw away the copy */
-		    if (mch_stat((char *)fname, &st) >= 0)
-			mch_remove(backup);
+		    * A forced write will try to create a new file if the old one is
+		    * still readonly. This may also happen when the directory is
+		    * read-only. In that case the mch_remove() will fail.
+		    */
+		    if (errmsg == NULL)
+		    {
+#ifdef UNIX
+			struct stat	st;
+
+			/* Don't delete the file when it's a hard or symbolic link. */
+			if ((!newfile && st_old.st_nlink > 1)
+				|| (mch_lstat((char *)fname, &st) == 0
+				    && (st.st_dev != st_old.st_dev
+					|| st.st_ino != st_old.st_ino)))
+			    errmsg = (char_u *)_("E166: Can't open linked file for writing");
+			else
+#endif
+			{
+			    errmsg = (char_u *)_("E212: Can't open file for writing");
+			    if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
+									    && perm >= 0)
+			    {
+#ifdef UNIX
+				/* we write to the file, thus it should be marked
+				writable after all */
+				if (!(perm & 0200))
+				    made_writable = TRUE;
+				perm |= 0200;
+				if (st_old.st_uid != getuid() || st_old.st_gid != getgid())
+				    perm &= 0777;
+#endif
+				if (!append)	    /* don't remove when appending */
+				    mch_remove(wfname);
+				continue;
+			    }
+			}
+		    }
+
+	    restore_backup:
+		    {
+			struct stat st;
+
+			/*
+			* If we failed to open the file, we don't need a backup. Throw it
+			* away.  If we moved or removed the original file try to put the
+			* backup in its place.
+			*/
+			if (backup != NULL && wfname == fname)
+			{
+			    if (backup_copy)
+			    {
+				/*
+				* There is a small chance that we removed the original,
+				* try to move the copy in its place.
+				* This may not work if the vim_rename() fails.
+				* In that case we leave the copy around.
+				*/
+				/* If file does not exist, put the copy in its place */
+				if (mch_stat((char *)fname, &st) < 0)
+				    vim_rename(backup, fname);
+				/* if original file does exist throw away the copy */
+				if (mch_stat((char *)fname, &st) >= 0)
+				    mch_remove(backup);
+			    }
+			    else
+			    {
+				/* try to put the original file back */
+				vim_rename(backup, fname);
+			    }
+			}
+
+			/* if original file no longer exists give an extra warning */
+			if (!newfile && mch_stat((char *)fname, &st) < 0)
+			    end = 0;
+		    }
+
+#ifdef FEAT_MBYTE
+		    if (wfname != fname)
+			vim_free(wfname);
+#endif
+		    goto fail;
 		}
-		else
-		{
-		    /* try to put the original file back */
-		    vim_rename(backup, fname);
-		}
-	    }
-
-	    /* if original file no longer exists give an extra warning */
-	    if (!newfile && mch_stat((char *)fname, &st) < 0)
-		end = 0;
-	}
-
-#ifdef FEAT_MBYTE
-	if (wfname != fname)
-	    vim_free(wfname);
-#endif
-	goto fail;
-    }
-    errmsg = NULL;
+	}
+	errmsg = NULL;
 
 #if defined(MACOS_CLASSIC) || defined(WIN3264)
-    /* TODO: Is it need for MACOS_X? (Dany) */
-    /*
-     * On macintosh copy the original files attributes (i.e. the backup)
-     * This is done in order to preserve the resource fork and the
-     * Finder attribute (label, comments, custom icons, file creator)
-     */
-    if (backup != NULL && overwriting && !append)
-    {
-	if (backup_copy)
-	    (void)mch_copy_file_attribute(wfname, backup);
-	else
-	    (void)mch_copy_file_attribute(backup, wfname);
-    }
-
-    if (!overwriting && !append)
-    {
-	if (buf->b_ffname != NULL)
-	    (void)mch_copy_file_attribute(buf->b_ffname, wfname);
-	/* Should copy resource fork */
-    }
-#endif
-
-    write_info.bw_fd = fd;
+	/* TODO: Is it need for MACOS_X? (Dany) */
+	/*
+	* On macintosh copy the original files attributes (i.e. the backup)
+	* This is done in order to preserve the resource fork and the
+	* Finder attribute (label, comments, custom icons, file creator)
+	*/
+	if (backup != NULL && overwriting && !append)
+	{
+	    if (backup_copy)
+		(void)mch_copy_file_attribute(wfname, backup);
+	    else
+		(void)mch_copy_file_attribute(backup, wfname);
+	}
+
+	if (!overwriting && !append)
+	{
+	    if (buf->b_ffname != NULL)
+		(void)mch_copy_file_attribute(buf->b_ffname, wfname);
+	    /* Should copy resource fork */
+	}
+#endif
+
+	write_info.bw_fd = fd;
 
 #ifdef FEAT_CRYPT
-    if (*buf->b_p_key != NUL && !filtering)
-    {
-	char_u *header;
-	int    header_len;
-
-	header = prepare_crypt_write(buf, &header_len);
-	if (header == NULL)
-	    end = 0;
-	else
-	{
-	    /* Write magic number, so that Vim knows that this file is
-	     * encrypted when reading it again.  This also undergoes utf-8 to
-	     * ucs-2/4 conversion when needed. */
-	    write_info.bw_buf = header;
-	    write_info.bw_len = header_len;
-	    write_info.bw_flags = FIO_NOCONVERT;
-	    if (buf_write_bytes(&write_info) == FAIL)
-		end = 0;
-	    wb_flags |= FIO_ENCRYPTED;
-	    vim_free(header);
-	}
-    }
-#endif
-
-    write_info.bw_buf = buffer;
-    nchars = 0;
-
-    /* use "++bin", "++nobin" or 'binary' */
-    if (eap != NULL && eap->force_bin != 0)
-	write_bin = (eap->force_bin == FORCE_BIN);
-    else
-	write_bin = buf->b_p_bin;
-
-#ifdef FEAT_MBYTE
-    /*
-     * The BOM is written just after the encryption magic number.
-     * Skip it when appending and the file already existed, the BOM only makes
-     * sense at the start of the file.
-     */
-    if (buf->b_p_bomb && !write_bin && (!append || perm < 0))
-    {
-	write_info.bw_len = make_bom(buffer, fenc);
-	if (write_info.bw_len > 0)
-	{
-	    /* don't convert, do encryption */
-	    write_info.bw_flags = FIO_NOCONVERT | wb_flags;
-	    if (buf_write_bytes(&write_info) == FAIL)
+	if (*buf->b_p_key != NUL && !filtering)
+	{
+	    char_u *header;
+	    int    header_len;
+
+	    header = prepare_crypt_write(buf, &header_len);
+	    if (header == NULL)
 		end = 0;
 	    else
-		nchars += write_info.bw_len;
-	}
-    }
-    write_info.bw_start_lnum = start;
+	    {
+		/* Write magic number, so that Vim knows that this file is
+		* encrypted when reading it again.  This also undergoes utf-8 to
+		* ucs-2/4 conversion when needed. */
+		write_info.bw_buf = header;
+		write_info.bw_len = header_len;
+		write_info.bw_flags = FIO_NOCONVERT;
+		if (buf_write_bytes(&write_info) == FAIL)
+		    end = 0;
+		wb_flags |= FIO_ENCRYPTED;
+		vim_free(header);
+	    }
+	}
+#endif
+
+	write_info.bw_buf = buffer;
+	nchars = 0;
+
+	/* use "++bin", "++nobin" or 'binary' */
+	if (eap != NULL && eap->force_bin != 0)
+	    write_bin = (eap->force_bin == FORCE_BIN);
+	else
+	    write_bin = buf->b_p_bin;
+
+#ifdef FEAT_MBYTE
+	/*
+	* The BOM is written just after the encryption magic number.
+	* Skip it when appending and the file already existed, the BOM only makes
+	* sense at the start of the file.
+	*/
+	if (buf->b_p_bomb && !write_bin && (!append || perm < 0))
+	{
+	    write_info.bw_len = make_bom(buffer, fenc);
+	    if (write_info.bw_len > 0)
+	    {
+		/* don't convert, do encryption */
+		write_info.bw_flags = FIO_NOCONVERT | wb_flags;
+		if (buf_write_bytes(&write_info) == FAIL)
+		    end = 0;
+		else
+		    nchars += write_info.bw_len;
+	    }
+	}
+	write_info.bw_start_lnum = start;
 #endif
 
 #ifdef FEAT_PERSISTENT_UNDO
-    write_undo_file = (buf->b_p_udf && overwriting && !append
-					      && !filtering && reset_changed);
-    if (write_undo_file)
-	/* Prepare for computing the hash value of the text. */
-	sha256_start(&sha_ctx);
-#endif
-
-    write_info.bw_len = bufsize;
+	write_undo_file = (buf->b_p_udf && overwriting && !append
+						&& !filtering && reset_changed);
+	if (write_undo_file)
+	    /* Prepare for computing the hash value of the text. */
+	    sha256_start(&sha_ctx);
+#endif
+
+	write_info.bw_len = bufsize;
 #ifdef HAS_BW_FLAGS
-    write_info.bw_flags = wb_flags;
-#endif
-    fileformat = get_fileformat_force(buf, eap);
-    s = buffer;
-    len = 0;
-    for (lnum = start; lnum <= end; ++lnum)
-    {
-	/*
-	 * The next while loop is done once for each character written.
-	 * Keep it fast!
-	 */
-	ptr = ml_get_buf(buf, lnum, FALSE) - 1;
+	write_info.bw_flags = wb_flags;
+#endif
+	fileformat = get_fileformat_force(buf, eap);
+	s = buffer;
+	len = 0;
+	for (lnum = start; lnum <= end; ++lnum)
+	{
+	    /*
+	    * The next while loop is done once for each character written.
+	    * Keep it fast!
+	    */
+	    ptr = ml_get_buf(buf, lnum, FALSE) - 1;
 #ifdef FEAT_PERSISTENT_UNDO
-	if (write_undo_file)
-	    sha256_update(&sha_ctx, ptr + 1, (UINT32_T)(STRLEN(ptr + 1) + 1));
-#endif
-	while ((c = *++ptr) != NUL)
-	{
-	    if (c == NL)
-		*s = NUL;		/* replace newlines with NULs */
-	    else if (c == CAR && fileformat == EOL_MAC)
-		*s = NL;		/* Mac: replace CRs with NLs */
+	    if (write_undo_file)
+		sha256_update(&sha_ctx, ptr + 1, (UINT32_T)(STRLEN(ptr + 1) + 1));
+#endif
+	    while ((c = *++ptr) != NUL)
+	    {
+		if (c == NL)
+		    *s = NUL;		/* replace newlines with NULs */
+		else if (c == CAR && fileformat == EOL_MAC)
+		    *s = NL;		/* Mac: replace CRs with NLs */
+		else
+		    *s = c;
+		++s;
+		if (++len != bufsize)
+		    continue;
+		if (buf_write_bytes(&write_info) == FAIL)
+		{
+		    end = 0;		/* write error: break loop */
+		    break;
+		}
+		nchars += bufsize;
+		s = buffer;
+		len = 0;
+#ifdef FEAT_MBYTE
+		write_info.bw_start_lnum = lnum;
+#endif
+	    }
+	    /* write failed or last line has no EOL: stop here */
+	    if (end == 0
+		    || (lnum == end
+			&& write_bin
+			&& (lnum == buf->b_no_eol_lnum
+			    || (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol))))
+	    {
+		++lnum;			/* written the line, count it */
+		no_eol = TRUE;
+		break;
+	    }
+	    if (fileformat == EOL_UNIX)
+		*s++ = NL;
 	    else
-		*s = c;
-	    ++s;
-	    if (++len != bufsize)
-		continue;
-	    if (buf_write_bytes(&write_info) == FAIL)
-	    {
-		end = 0;		/* write error: break loop */
-		break;
-	    }
-	    nchars += bufsize;
-	    s = buffer;
-	    len = 0;
-#ifdef FEAT_MBYTE
-	    write_info.bw_start_lnum = lnum;
-#endif
-	}
-	/* write failed or last line has no EOL: stop here */
-	if (end == 0
-		|| (lnum == end
-		    && write_bin
-		    && (lnum == buf->b_no_eol_lnum
-			|| (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol))))
-	{
-	    ++lnum;			/* written the line, count it */
-	    no_eol = TRUE;
-	    break;
-	}
-	if (fileformat == EOL_UNIX)
-	    *s++ = NL;
-	else
-	{
-	    *s++ = CAR;			/* EOL_MAC or EOL_DOS: write CR */
-	    if (fileformat == EOL_DOS)	/* write CR-NL */
-	    {
-		if (++len == bufsize)
+	    {
+		*s++ = CAR;			/* EOL_MAC or EOL_DOS: write CR */
+		if (fileformat == EOL_DOS)	/* write CR-NL */
 		{
+		    if (++len == bufsize)
+		    {
+			if (buf_write_bytes(&write_info) == FAIL)
+			{
+			    end = 0;	/* write error: break loop */
+			    break;
+			}
+			nchars += bufsize;
+			s = buffer;
+			len = 0;
+		    }
+		    *s++ = NL;
+		}
+	    }
+	    if (++len == bufsize && end)
+	    {
+		if (buf_write_bytes(&write_info) == FAIL)
+		{
+		    end = 0;		/* write error: break loop */
+		    break;
+		}
+		nchars += bufsize;
+		s = buffer;
+		len = 0;
+
+		ui_breakcheck();
+		if (got_int)
+		{
+		    end = 0;		/* Interrupted, break loop */
+		    break;
+		}
+	    }
+#ifdef VMS
+	    /*
+	    * On VMS there is a problem: newlines get added when writing blocks
+	    * at a time. Fix it by writing a line at a time.
+	    * This is much slower!
+	    * Explanation: VAX/DECC RTL insists that records in some RMS
+	    * structures end with a newline (carriage return) character, and if
+	    * they don't it adds one.
+	    * With other RMS structures it works perfect without this fix.
+	    */
+	    if (buf->b_fab_rfm == FAB$C_VFC
+		    || ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0))
+	    {
+		int b2write;
+
+		buf->b_fab_mrs = (buf->b_fab_mrs == 0
+			? MIN(4096, bufsize)
+			: MIN(buf->b_fab_mrs, bufsize));
+
+		b2write = len;
+		while (b2write > 0)
+		{
+		    write_info.bw_len = MIN(b2write, buf->b_fab_mrs);
 		    if (buf_write_bytes(&write_info) == FAIL)
 		    {
-			end = 0;	/* write error: break loop */
+			end = 0;
 			break;
 		    }
-		    nchars += bufsize;
-		    s = buffer;
-		    len = 0;
+		    b2write -= MIN(b2write, buf->b_fab_mrs);
 		}
-		*s++ = NL;
-	    }
-	}
-	if (++len == bufsize && end)
-	{
+		write_info.bw_len = bufsize;
+		nchars += len;
+		s = buffer;
+		len = 0;
+	    }
+#endif
+	}
+	if (len > 0 && end > 0)
+	{
+	    write_info.bw_len = len;
 	    if (buf_write_bytes(&write_info) == FAIL)
-	    {
-		end = 0;		/* write error: break loop */
-		break;
-	    }
-	    nchars += bufsize;
-	    s = buffer;
-	    len = 0;
-
-	    ui_breakcheck();
-	    if (got_int)
-	    {
-		end = 0;		/* Interrupted, break loop */
-		break;
-	    }
-	}
-#ifdef VMS
-	/*
-	 * On VMS there is a problem: newlines get added when writing blocks
-	 * at a time. Fix it by writing a line at a time.
-	 * This is much slower!
-	 * Explanation: VAX/DECC RTL insists that records in some RMS
-	 * structures end with a newline (carriage return) character, and if
-	 * they don't it adds one.
-	 * With other RMS structures it works perfect without this fix.
-	 */
-	if (buf->b_fab_rfm == FAB$C_VFC
-		|| ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0))
-	{
-	    int b2write;
-
-	    buf->b_fab_mrs = (buf->b_fab_mrs == 0
-		    ? MIN(4096, bufsize)
-		    : MIN(buf->b_fab_mrs, bufsize));
-
-	    b2write = len;
-	    while (b2write > 0)
-	    {
-		write_info.bw_len = MIN(b2write, buf->b_fab_mrs);
-		if (buf_write_bytes(&write_info) == FAIL)
+		end = 0;		    /* write error */
+	    nchars += len;
+	}
+
+	/* conversion error */
+	if (!end)
+	  break;
+
+	/* If no error happened until now, writing should be ok,
+	 * so start outer for loop over again, this time really
+	 * writing the buffer */
+	if (!check_conv)
+	  continue;
+
+
+#if defined(UNIX) && defined(HAVE_FSYNC)
+	/* On many journalling file systems there is a bug that causes both the
+	* original and the backup file to be lost when halting the system right
+	* after writing the file.  That's because only the meta-data is
+	* journalled.  Syncing the file slows down the system, but assures it has
+	* been written to disk and we don't lose it.
+	* For a device do try the fsync() but don't complain if it does not work
+	* (could be a pipe).
+	* If the 'fsync' option is FALSE, don't fsync().  Useful for laptops. */
+	if (p_fs && fsync(fd) != 0 && !device)
+	{
+	    errmsg = (char_u *)_("E667: Fsync failed");
+	    end = 0;
+	}
+#endif
+
+#ifdef HAVE_SELINUX
+	/* Probably need to set the security context. */
+	if (!backup_copy)
+	    mch_copy_sec(backup, wfname);
+#endif
+
+#ifdef UNIX
+	/* When creating a new file, set its owner/group to that of the original
+	* file.  Get the new device and inode number. */
+	if (backup != NULL && !backup_copy)
+	{
+#ifdef HAVE_FCHOWN
+	    struct stat	st;
+
+	    /* don't change the owner when it's already OK, some systems remove
+	    * permission or ACL stuff */
+	    if (mch_stat((char *)wfname, &st) < 0
+		    || st.st_uid != st_old.st_uid
+		    || st.st_gid != st_old.st_gid)
+	    {
+		ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
+		if (perm >= 0)	/* set permission again, may have changed */
+		    (void)mch_setperm(wfname, perm);
+	    }
+#endif
+	    buf_setino(buf);
+	}
+	else if (!buf->b_dev_valid)
+	    /* Set the inode when creating a new file. */
+	    buf_setino(buf);
+#endif
+
+	if (end > 0 && close(fd) != 0)
+	{
+	    errmsg = (char_u *)_("E512: Close failed");
+	    end = 0;
+	}
+
+#ifdef UNIX
+	if (made_writable)
+	    perm &= ~0200;		/* reset 'w' bit for security reasons */
+#endif
+	if (perm >= 0)		/* set perm. of new file same as old file */
+	    (void)mch_setperm(wfname, perm);
+#ifdef HAVE_ACL
+	/* Probably need to set the ACL before changing the user (can't set the
+	* ACL on a file the user doesn't own). */
+	if (!backup_copy)
+	    mch_set_acl(wfname, acl);
+#endif
+#ifdef FEAT_CRYPT
+	crypt_method_used = use_crypt_method;
+	if (wb_flags & FIO_ENCRYPTED)
+	    crypt_pop_state();
+#endif
+
+
+#if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
+	if (wfname != fname)
+	{
+	    /*
+	    * The file was written to a temp file, now it needs to be converted
+	    * with 'charconvert' to (overwrite) the output file.
+	    */
+	    if (end != 0)
+	    {
+		if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc,
+							wfname, fname) == FAIL)
 		{
+		    write_info.bw_conv_error = TRUE;
 		    end = 0;
-		    break;
 		}
-		b2write -= MIN(b2write, buf->b_fab_mrs);
-	    }
-	    write_info.bw_len = bufsize;
-	    nchars += len;
-	    s = buffer;
-	    len = 0;
-	}
-#endif
-    }
-    if (len > 0 && end > 0)
-    {
-	write_info.bw_len = len;
-	if (buf_write_bytes(&write_info) == FAIL)
-	    end = 0;		    /* write error */
-	nchars += len;
-    }
-
-#if defined(UNIX) && defined(HAVE_FSYNC)
-    /* On many journalling file systems there is a bug that causes both the
-     * original and the backup file to be lost when halting the system right
-     * after writing the file.  That's because only the meta-data is
-     * journalled.  Syncing the file slows down the system, but assures it has
-     * been written to disk and we don't lose it.
-     * For a device do try the fsync() but don't complain if it does not work
-     * (could be a pipe).
-     * If the 'fsync' option is FALSE, don't fsync().  Useful for laptops. */
-    if (p_fs && fsync(fd) != 0 && !device)
-    {
-	errmsg = (char_u *)_("E667: Fsync failed");
-	end = 0;
-    }
-#endif
-
-#ifdef HAVE_SELINUX
-    /* Probably need to set the security context. */
-    if (!backup_copy)
-	mch_copy_sec(backup, wfname);
-#endif
-
-#ifdef UNIX
-    /* When creating a new file, set its owner/group to that of the original
-     * file.  Get the new device and inode number. */
-    if (backup != NULL && !backup_copy)
-    {
-# ifdef HAVE_FCHOWN
-	struct stat	st;
-
-	/* don't change the owner when it's already OK, some systems remove
-	 * permission or ACL stuff */
-	if (mch_stat((char *)wfname, &st) < 0
-		|| st.st_uid != st_old.st_uid
-		|| st.st_gid != st_old.st_gid)
-	{
-	    ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
-	    if (perm >= 0)	/* set permission again, may have changed */
-		(void)mch_setperm(wfname, perm);
-	}
-# endif
-	buf_setino(buf);
-    }
-    else if (!buf->b_dev_valid)
-	/* Set the inode when creating a new file. */
-	buf_setino(buf);
-#endif
-
-    if (close(fd) != 0)
-    {
-	errmsg = (char_u *)_("E512: Close failed");
-	end = 0;
-    }
-
-#ifdef UNIX
-    if (made_writable)
-	perm &= ~0200;		/* reset 'w' bit for security reasons */
-#endif
-    if (perm >= 0)		/* set perm. of new file same as old file */
-	(void)mch_setperm(wfname, perm);
-#ifdef HAVE_ACL
-    /* Probably need to set the ACL before changing the user (can't set the
-     * ACL on a file the user doesn't own). */
-    if (!backup_copy)
-	mch_set_acl(wfname, acl);
-#endif
-#ifdef FEAT_CRYPT
-    crypt_method_used = use_crypt_method;
-    if (wb_flags & FIO_ENCRYPTED)
-	crypt_pop_state();
-#endif
-
-
-#if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
-    if (wfname != fname)
-    {
-	/*
-	 * The file was written to a temp file, now it needs to be converted
-	 * with 'charconvert' to (overwrite) the output file.
-	 */
-	if (end != 0)
-	{
-	    if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc,
-						       wfname, fname) == FAIL)
-	    {
-		write_info.bw_conv_error = TRUE;
-		end = 0;
-	    }
-	}
-	mch_remove(wfname);
-	vim_free(wfname);
-    }
-#endif
+	    }
+	    mch_remove(wfname);
+	    vim_free(wfname);
+	}
+#endif
+    }
 
     if (end == 0)
     {
@@ -4779,6 +4811,10 @@
 		    errmsg = (char_u *)_("E514: write error (file system full?)");
 	}
 
+	if (end == 0) /* write error */
+	  goto fail;
+	  
+
 	/*
 	 * If we have a backup file, try to put it in place of the new file,
 	 * because the new file is probably corrupt.  This avoids losing the
@@ -5702,8 +5738,13 @@
 	crypt_encode(buf, len, buf);
 #endif
 
-    wlen = write_eintr(ip->bw_fd, buf, len);
-    return (wlen < len) ? FAIL : OK;
+    if (ip->do_write)
+    {
+	wlen = write_eintr(ip->bw_fd, buf, len);
+	return (wlen < len) ? FAIL : OK;
+    }
+    /* no error, writing and converting file should work ok */
+    return OK;
 }
 
 #ifdef FEAT_MBYTE

Reply via email to