To: vim_dev@googlegroups.com Subject: Patch 8.0.0685 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.0685 Problem: When making backups is disabled and conversion with iconv fails the written file is truncated. (Luo Chen) Solution: First try converting the file and write the file only when it did not fail. (partly by Christian Brabandt) Files: src/fileio.c, src/testdir/test_writefile.vim *** ../vim-8.0.0684/src/fileio.c 2017-04-07 19:50:08.691049319 +0200 --- src/fileio.c 2017-06-27 21:23:26.649445699 +0200 *************** *** 3166,3171 **** --- 3166,3172 ---- int device = FALSE; /* writing to a device */ stat_T st_old; int prev_got_int = got_int; + int checking_conversion; int file_readonly = FALSE; /* overwritten file is read-only */ static char *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')"; #if defined(UNIX) /*XXX fix me sometime? */ *************** *** 4344,4776 **** #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. ! */ ! 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 ! stat_T 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: - { - stat_T 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; ! } ! 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; - #ifdef FEAT_CRYPT ! if (*buf->b_p_key != NUL && !filtering) ! { ! char_u *header; ! int header_len; ! buf->b_cryptstate = crypt_create_for_writing(crypt_get_method_nr(buf), ! buf->b_p_key, &header, &header_len); ! if (buf->b_cryptstate == NULL || header == NULL) ! end = 0; ! else ! { ! /* Write magic number, so that Vim knows how this file is ! * encrypted when reading it back. */ ! 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; #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; #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 */ ! 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 || !buf->b_p_fixeol) ! && (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) { ! 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; ! 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 #if defined(HAVE_SELINUX) || defined(HAVE_SMACK) ! /* 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 ! stat_T 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). ! * On Solaris, with ZFS and the aclmode property set to "discard" (the ! * default), chmod() discards all part of a file's ACL that don't represent ! * the mode of the file. It's non-trivial for us to discover whether we're ! * in that situation, so we simply always re-set the ACL. ! */ # ifndef HAVE_SOLARIS_ZFS_ACL ! if (!backup_copy) # endif ! mch_set_acl(wfname, acl); #endif #ifdef FEAT_CRYPT ! if (buf->b_cryptstate != NULL) ! { ! crypt_free_state(buf->b_cryptstate); ! buf->b_cryptstate = NULL; ! } #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 if (end == 0) { if (errmsg == NULL) { #ifdef FEAT_MBYTE --- 4345,4835 ---- #endif /* ! * If conversion is taking place, we may first pretend to write and check ! * for conversion errors. Then loop again to write for real. ! * When not doing conversion this writes for real right away. ! */ ! for (checking_conversion = TRUE; ; checking_conversion = FALSE) { /* ! * There is no need to check conversion when: ! * - there is no conversion ! * - we make a backup file, that can be restored in case of conversion ! * failure. */ ! #ifdef FEAT_MBYTE ! if (!converted || dobackup) #endif + checking_conversion = FALSE; + + if (checking_conversion) + { + /* Make sure we don't write anything. */ + fd = -1; + write_info.bw_fd = fd; + } + else + { + /* + * 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) { ! /* ! * 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 ! stat_T 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: { + stat_T 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; ! } ! write_info.bw_fd = fd; #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 #ifdef FEAT_CRYPT ! if (*buf->b_p_key != NUL && !filtering) ! { ! char_u *header; ! int header_len; ! buf->b_cryptstate = crypt_create_for_writing( ! crypt_get_method_nr(buf), ! buf->b_p_key, &header, &header_len); ! if (buf->b_cryptstate == NULL || header == NULL) ! end = 0; ! else ! { ! /* Write magic number, so that Vim knows how this file is ! * encrypted when reading it back. */ ! 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 + } + errmsg = NULL; ! 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 ! && !checking_conversion); ! 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; #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 */ ! 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 || !buf->b_p_fixeol) ! && (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) { ! 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; ! 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; } ! ! /* Stop when writing done or an error was encountered. */ ! if (!checking_conversion || end == 0) ! break; ! ! /* If no error happened until now, writing should be ok, so loop to ! * really write the buffer. */ } ! /* If we started writing, finish writing. Also when an error was ! * encountered. */ ! if (!checking_conversion) { ! #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 #if defined(HAVE_SELINUX) || defined(HAVE_SMACK) ! /* 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 ! stat_T 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). ! * On Solaris, with ZFS and the aclmode property set to "discard" (the ! * default), chmod() discards all part of a file's ACL that don't ! * represent the mode of the file. It's non-trivial for us to discover ! * whether we're in that situation, so we simply always re-set the ACL. ! */ # ifndef HAVE_SOLARIS_ZFS_ACL ! if (!backup_copy) # endif ! mch_set_acl(wfname, acl); #endif #ifdef FEAT_CRYPT ! if (buf->b_cryptstate != NULL) ! { ! crypt_free_state(buf->b_cryptstate); ! buf->b_cryptstate = NULL; ! } #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 + } if (end == 0) { + /* + * Error encountered. + */ if (errmsg == NULL) { #ifdef FEAT_MBYTE *************** *** 5690,5695 **** --- 5749,5758 ---- } #endif /* FEAT_MBYTE */ + if (ip->bw_fd < 0) + /* Only checking conversion, which is OK if we get here. */ + return OK; + #ifdef FEAT_CRYPT if (flags & FIO_ENCRYPTED) { *** ../vim-8.0.0684/src/testdir/test_writefile.vim 2017-06-13 19:38:33.301791799 +0200 --- src/testdir/test_writefile.vim 2017-06-27 21:22:55.749684062 +0200 *************** *** 31,33 **** --- 31,51 ---- call assert_fails('call writefile([], [])', 'E730:') endfunc + + func Test_writefile_fails_conversion() + if !has('multi_byte') || !has('iconv') + return + endif + set nobackup nowritebackup + new + let contents = ["line one", "line two"] + call writefile(contents, 'Xfile') + edit Xfile + call setline(1, ["first line", "cannot convert \u010b", "third line"]) + call assert_fails('write ++enc=cp932') + call assert_equal(contents, readfile('Xfile')) + + call delete('Xfile') + bwipe! + set backup& writebackup& + endfunc *** ../vim-8.0.0684/src/version.c 2017-06-27 18:28:52.558196805 +0200 --- src/version.c 2017-06-27 22:11:05.711318535 +0200 *************** *** 766,767 **** --- 766,769 ---- { /* Add new patch number below this line */ + /**/ + 685, /**/ -- From "know your smileys": =):-) Uncle Sam /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///