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