# HG changeset patch # User Bram Moolenaar # Date 1558993504 -7200 # Node ID b1b7c7a316792aa4eff4be083f1eb9d4c9a24c0c # Parent 26751afbadf0ee5ca867653bda5f476229ed2159 patch 8.1.1413: error when the drive of the swap file was disconnected commit https://github.com/vim/vim/commit/b58a4b938c4bc7e0499700859bd7abba9acc5b11 Author: Bram Moolenaar Date: Mon May 27 23:36:21 2019 +0200 patch 8.1.1413: error when the drive of the swap file was disconnected Problem: Error when the drive of the swap file was disconnected. Solution: Try closing and re-opening the swap file. (closes https://github.com/vim/vim/issues/4378) diff --git a/src/memfile.c b/src/memfile.c --- a/src/memfile.c +++ b/src/memfile.c @@ -994,7 +994,8 @@ mf_write(memfile_T *mfp, bhdr_T *hp) unsigned page_count; /* number of pages written */ unsigned size; /* number of bytes written */ - if (mfp->mf_fd < 0) /* there is no file, can't write */ + if (mfp->mf_fd < 0 && !mfp->mf_reopen) + // there is no file and there was no file, can't write return FAIL; if (hp->bh_bnum < 0) /* must assign file block number */ @@ -1011,6 +1012,8 @@ mf_write(memfile_T *mfp, bhdr_T *hp) */ for (;;) { + int attempt; + nr = hp->bh_bnum; if (nr > mfp->mf_infile_count) /* beyond end of file */ { @@ -1021,29 +1024,49 @@ mf_write(memfile_T *mfp, bhdr_T *hp) hp2 = hp; offset = (off_T)page_size * nr; - if (vim_lseek(mfp->mf_fd, offset, SEEK_SET) != offset) - { - PERROR(_("E296: Seek error in swap file write")); - return FAIL; - } if (hp2 == NULL) /* freed block, fill with dummy data */ page_count = 1; else page_count = hp2->bh_page_count; size = page_size * page_count; - if (mf_write_block(mfp, hp2 == NULL ? hp : hp2, offset, size) == FAIL) + + for (attempt = 1; attempt <= 2; ++attempt) { - /* - * Avoid repeating the error message, this mostly happens when the - * disk is full. We give the message again only after a successful - * write or when hitting a key. We keep on trying, in case some - * space becomes available. - */ - if (!did_swapwrite_msg) - emsg(_("E297: Write error in swap file")); - did_swapwrite_msg = TRUE; - return FAIL; + if (mfp->mf_fd >= 0) + { + if (vim_lseek(mfp->mf_fd, offset, SEEK_SET) != offset) + { + PERROR(_("E296: Seek error in swap file write")); + return FAIL; + } + if (mf_write_block(mfp, + hp2 == NULL ? hp : hp2, offset, size) == OK) + break; + } + + if (attempt == 1) + { + // If the swap file is on a network drive, and the network + // gets disconnected and then re-connected, we can maybe fix it + // by closing and then re-opening the file. + if (mfp->mf_fd >= 0) + close(mfp->mf_fd); + mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, mfp->mf_flags); + mfp->mf_reopen = (mfp->mf_fd < 0); + } + if (attempt == 2 || mfp->mf_fd < 0) + { + // Avoid repeating the error message, this mostly happens when + // the disk is full. We give the message again only after a + // successful write or when hitting a key. We keep on trying, + // in case some space becomes available. + if (!did_swapwrite_msg) + emsg(_("E297: Write error in swap file")); + did_swapwrite_msg = TRUE; + return FAIL; + } } + did_swapwrite_msg = FALSE; if (hp2 != NULL) /* written a non-dummy block */ hp2->bh_flags &= ~BH_DIRTY; @@ -1271,6 +1294,7 @@ mf_do_open( * the file) */ flags |= O_NOINHERIT; #endif + mfp->mf_flags = flags; mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags); } diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -604,28 +604,30 @@ typedef struct struct memfile { - char_u *mf_fname; /* name of the file */ - char_u *mf_ffname; /* idem, full path */ - int mf_fd; /* file descriptor */ - bhdr_T *mf_free_first; /* first block_hdr in free list */ - bhdr_T *mf_used_first; /* mru block_hdr in used list */ - bhdr_T *mf_used_last; /* lru block_hdr in used list */ - unsigned mf_used_count; /* number of pages in used list */ - unsigned mf_used_count_max; /* maximum number of pages in memory */ - mf_hashtab_T mf_hash; /* hash lists */ - mf_hashtab_T mf_trans; /* trans lists */ - blocknr_T mf_blocknr_max; /* highest positive block number + 1*/ - blocknr_T mf_blocknr_min; /* lowest negative block number - 1 */ - blocknr_T mf_neg_count; /* number of negative blocks numbers */ - blocknr_T mf_infile_count; /* number of pages in the file */ - unsigned mf_page_size; /* number of bytes in a page */ - int mf_dirty; /* TRUE if there are dirty blocks */ + char_u *mf_fname; // name of the file + char_u *mf_ffname; // idem, full path + int mf_fd; // file descriptor + int mf_flags; // flags used when opening this memfile + int mf_reopen; // mf_fd was closed, retry opening + bhdr_T *mf_free_first; // first block_hdr in free list + bhdr_T *mf_used_first; // mru block_hdr in used list + bhdr_T *mf_used_last; // lru block_hdr in used list + unsigned mf_used_count; // number of pages in used list + unsigned mf_used_count_max; // maximum number of pages in memory + mf_hashtab_T mf_hash; // hash lists + mf_hashtab_T mf_trans; // trans lists + blocknr_T mf_blocknr_max; // highest positive block number + 1 + blocknr_T mf_blocknr_min; // lowest negative block number - 1 + blocknr_T mf_neg_count; // number of negative blocks numbers + blocknr_T mf_infile_count; // number of pages in the file + unsigned mf_page_size; // number of bytes in a page + int mf_dirty; // TRUE if there are dirty blocks #ifdef FEAT_CRYPT - buf_T *mf_buffer; /* buffer this memfile is for */ - char_u mf_seed[MF_SEED_LEN]; /* seed for encryption */ - - /* Values for key, method and seed used for reading data blocks when - * updating for a newly set key or method. Only when mf_old_key != NULL. */ + buf_T *mf_buffer; // buffer this memfile is for + char_u mf_seed[MF_SEED_LEN]; // seed for encryption + + // Values for key, method and seed used for reading data blocks when + // updating for a newly set key or method. Only when mf_old_key != NULL. char_u *mf_old_key; int mf_old_cm; char_u mf_old_seed[MF_SEED_LEN]; diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -288,24 +288,24 @@ func Test_q_arg() " Test with default argument '-q'. call assert_equal('errors.err', &errorfile) - call writefile(["../memfile.c:1482:5: error: expected ';' before '}' token"], 'errors.err') + call writefile(["../memfile.c:208:5: error: expected ';' before '}' token"], 'errors.err') if RunVim([], after, '-q') let lines = readfile('Xtestout') call assert_equal(['errors.err', - \ '[0, 1482, 5, 0]', - \ source_file . "|1482 col 5| error: expected ';' before '}' token"], + \ '[0, 208, 5, 0]', + \ source_file . "|208 col 5| error: expected ';' before '}' token"], \ lines) endif call delete('Xtestout') call delete('errors.err') " Test with explicit argument '-q Xerrors' (with space). - call writefile(["../memfile.c:1482:5: error: expected ';' before '}' token"], 'Xerrors') + call writefile(["../memfile.c:208:5: error: expected ';' before '}' token"], 'Xerrors') if RunVim([], after, '-q Xerrors') let lines = readfile('Xtestout') call assert_equal(['Xerrors', - \ '[0, 1482, 5, 0]', - \ source_file . "|1482 col 5| error: expected ';' before '}' token"], + \ '[0, 208, 5, 0]', + \ source_file . "|208 col 5| error: expected ';' before '}' token"], \ lines) endif call delete('Xtestout') @@ -314,8 +314,8 @@ func Test_q_arg() if RunVim([], after, '-qXerrors') let lines = readfile('Xtestout') call assert_equal(['Xerrors', - \ '[0, 1482, 5, 0]', - \ source_file . "|1482 col 5| error: expected ';' before '}' token"], + \ '[0, 208, 5, 0]', + \ source_file . "|208 col 5| error: expected ';' before '}' token"], \ lines) endif diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -768,6 +768,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1413, +/**/ 1412, /**/ 1411,