Merge pull request #2117 from justinmk/fix1836

memline: fix segfaults
This commit is contained in:
Justin M. Keyes 2015-04-12 02:48:34 -04:00
commit ecc28fb2dd
8 changed files with 181 additions and 98 deletions

View File

@ -824,7 +824,7 @@ void ml_recover(void)
(void)recover_names(fname, FALSE, i, &fname_used); (void)recover_names(fname, FALSE, i, &fname_used);
} }
if (fname_used == NULL) if (fname_used == NULL)
goto theend; /* out of memory */ goto theend; // user chose invalid number.
/* When called from main() still need to initialize storage structure */ /* When called from main() still need to initialize storage structure */
if (called_from_main && ml_open(curbuf) == FAIL) if (called_from_main && ml_open(curbuf) == FAIL)
@ -1244,8 +1244,10 @@ theend:
mf_put(mfp, hp, false, false); mf_put(mfp, hp, false, false);
mf_close(mfp, false); /* will also free(mfp->mf_fname) */ mf_close(mfp, false); /* will also free(mfp->mf_fname) */
} }
free(buf->b_ml.ml_stack); if (buf != NULL) { //may be NULL if swap file not found.
free(buf); free(buf->b_ml.ml_stack);
free(buf);
}
if (serious_error && called_from_main) if (serious_error && called_from_main)
ml_close(curbuf, TRUE); ml_close(curbuf, TRUE);
else { else {
@ -1321,53 +1323,35 @@ recover_names (
if (dir_name[0] == '.' && dir_name[1] == NUL) { /* check current dir */ if (dir_name[0] == '.' && dir_name[1] == NUL) { /* check current dir */
if (fname == NULL) { if (fname == NULL) {
names[0] = vim_strsave((char_u *)"*.sw?"); names[0] = vim_strsave((char_u *)"*.sw?");
#if defined(UNIX) || defined(WIN3264)
/* For Unix names starting with a dot are special. MS-Windows /* For Unix names starting with a dot are special. MS-Windows
* supports this too, on some file systems. */ * supports this too, on some file systems. */
names[1] = vim_strsave((char_u *)".*.sw?"); names[1] = vim_strsave((char_u *)".*.sw?");
names[2] = vim_strsave((char_u *)".sw?"); names[2] = vim_strsave((char_u *)".sw?");
num_names = 3; num_names = 3;
#else
num_names = 1;
#endif
} else } else
num_names = recov_file_names(names, fname_res, TRUE); num_names = recov_file_names(names, fname_res, TRUE);
} else { /* check directory dir_name */ } else { /* check directory dir_name */
if (fname == NULL) { if (fname == NULL) {
names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE); names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
#if defined(UNIX) || defined(WIN3264)
/* For Unix names starting with a dot are special. MS-Windows /* For Unix names starting with a dot are special. MS-Windows
* supports this too, on some file systems. */ * supports this too, on some file systems. */
names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE); names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE); names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
num_names = 3; num_names = 3;
#else
num_names = 1;
#endif
} else { } else {
#if defined(UNIX) || defined(WIN3264)
p = dir_name + STRLEN(dir_name); p = dir_name + STRLEN(dir_name);
if (after_pathsep(dir_name, p) && p[-1] == p[-2]) { if (after_pathsep(dir_name, p) && p[-1] == p[-2]) {
/* Ends with '//', Use Full path for swap name */ /* Ends with '//', Use Full path for swap name */
tail = make_percent_swname(dir_name, fname_res); tail = make_percent_swname(dir_name, fname_res);
} else } else {
#endif tail = path_tail(fname_res);
tail = path_tail(fname_res); tail = concat_fnames(dir_name, tail, TRUE);
tail = concat_fnames(dir_name, tail, TRUE); }
num_names = recov_file_names(names, tail, FALSE); num_names = recov_file_names(names, tail, FALSE);
free(tail); free(tail);
} }
} }
// check for out-of-memory
for (int i = 0; i < num_names; ++i) {
if (names[i] == NULL) {
for (int j = 0; j < num_names; ++j)
free(names[j]);
num_names = 0;
break;
}
}
if (num_names == 0) if (num_names == 0)
num_files = 0; num_files = 0;
else if (expand_wildcards(num_names, names, &num_files, &files, else if (expand_wildcards(num_names, names, &num_files, &files,
@ -1453,7 +1437,6 @@ recover_names (
return file_count; return file_count;
} }
#if defined(UNIX) || defined(WIN3264) /* Need _very_ long file names */
/* /*
* Append the full path to name with path separators made into percent * Append the full path to name with path separators made into percent
* signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"") * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"")
@ -1475,7 +1458,6 @@ static char_u *make_percent_swname(char_u *dir, char_u *name)
} }
return d; return d;
} }
#endif
#ifdef UNIX #ifdef UNIX
static int process_still_running; static int process_still_running;
@ -1576,17 +1558,12 @@ static time_t swapfile_info(char_u *fname)
} }
static int recov_file_names(char_u **names, char_u *path, int prepend_dot) static int recov_file_names(char_u **names, char_u *path, int prepend_dot)
FUNC_ATTR_NONNULL_ALL
{ {
int num_names; int num_names = 0;
char_u *p;
int i;
num_names = 0; // May also add the file name with a dot prepended, for swap file in same
// dir as original file.
/*
* May also add the file name with a dot prepended, for swap file in same
* dir as original file.
*/
if (prepend_dot) { if (prepend_dot) {
names[num_names] = modname(path, (char_u *)".sw?", TRUE); names[num_names] = modname(path, (char_u *)".sw?", TRUE);
if (names[num_names] == NULL) if (names[num_names] == NULL)
@ -1597,8 +1574,8 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot)
// Form the normal swap file name pattern by appending ".sw?". // Form the normal swap file name pattern by appending ".sw?".
names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE); names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);
if (num_names >= 1) { /* check if we have the same name twice */ if (num_names >= 1) { /* check if we have the same name twice */
p = names[num_names - 1]; char_u *p = names[num_names - 1];
i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]); int i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
if (i > 0) if (i > 0)
p += i; /* file name has been expanded to full path */ p += i; /* file name has been expanded to full path */
@ -3088,7 +3065,6 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name
char_u fname_buf[MAXPATHL]; char_u fname_buf[MAXPATHL];
#endif #endif
#if defined(UNIX) || defined(WIN3264) /* Need _very_ long file names */
s = dir_name + STRLEN(dir_name); s = dir_name + STRLEN(dir_name);
if (after_pathsep(dir_name, s) && s[-1] == s[-2]) { /* Ends with '//', Use Full path */ if (after_pathsep(dir_name, s) && s[-1] == s[-2]) { /* Ends with '//', Use Full path */
r = NULL; r = NULL;
@ -3098,7 +3074,6 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name
} }
return r; return r;
} }
#endif
#ifdef HAVE_READLINK #ifdef HAVE_READLINK
/* Expand symlink in the file name, so that we put the swap file with the /* Expand symlink in the file name, so that we put the swap file with the

View File

@ -83,15 +83,12 @@ FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname)
return kDifferentFiles; return kDifferentFiles;
} }
/// Get the tail of a path: the file name. /// Gets the tail (i.e., the filename segment) of a path `fname`.
/// ///
/// @param fname A file path. /// @return pointer just past the last path separator (empty string, if fname
/// @return /// ends in a slash), or empty string if fname is NULL.
/// - Empty string, if fname is NULL.
/// - The position of the last path separator + 1. (i.e. empty string, if
/// fname ends in a slash).
/// - Never NULL.
char_u *path_tail(char_u *fname) char_u *path_tail(char_u *fname)
FUNC_ATTR_NONNULL_RET
{ {
if (fname == NULL) { if (fname == NULL) {
return (char_u *)""; return (char_u *)"";

View File

@ -24,7 +24,7 @@ SCRIPTS := test_autoformat_join.out \
test61.out test62.out test63.out test64.out test65.out \ test61.out test62.out test63.out test64.out test65.out \
test68.out test69.out \ test68.out test69.out \
test71.out test73.out test74.out \ test71.out test73.out test74.out \
test76.out test78.out test79.out test80.out \ test76.out test79.out test80.out \
test82.out test83.out \ test82.out test83.out \
test86.out test87.out test88.out \ test86.out test87.out test88.out \
test96.out \ test96.out \

View File

@ -1,46 +0,0 @@
Inserts 10000 lines with text to fill the swap file with two levels of pointer
blocks. Then recovers from the swap file and checks all text is restored.
We need about 10000 lines of 100 characters to get two levels of pointer
blocks.
STARTTEST
:so small.vim
:set fileformat=unix undolevels=-1
:e! Xtest
ggdG
:let text = "\tabcdefghijklmnoparstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnoparstuvwxyz0123456789"
:let i = 1
:let linecount = 10000
:while i <= linecount | call append(i - 1, i . text) | let i += 1 | endwhile
:preserve
:" get the name of the swap file
:redir => swapname
:swapname
:redir END
:let swapname = substitute(swapname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
:" make a copy of the swap file in Xswap
:set bin
:exe 'sp ' . swapname
:w! Xswap
:echo swapname
:set nobin
:new
:only!
:bwipe! Xtest
:call rename('Xswap', swapname)
:recover Xtest
:call delete(swapname)
:new
:call append(0, 'recovery start')
:wincmd w
:let linedollar = line('$')
:if linedollar < linecount | exe 'wincmd w' | call append(line('$'), "expected " . linecount . " lines but found only " . linedollar) | exe 'wincmd w' | let linecount = linedollar | endif
:let i = 1
:while i <= linecount | if getline(i) != i . text | exe 'wincmd w' | call append(line('$'), i . ' differs') | exe 'wincmd w' | endif | let i += 1 | endwhile
:q!
:call append(line('$'), 'recovery end')
:w! test.out
:qa!
ENDTEST

View File

@ -1,3 +0,0 @@
recovery start
recovery end

View File

@ -0,0 +1,71 @@
-- Tests for :recover
local helpers = require('test.functional.helpers')
local execute, eq, clear, eval, feed, expect, source =
helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.feed,
helpers.expect, helpers.source
describe(':recover', function()
before_each(clear)
it('fails if given a non-existent swapfile', function()
local swapname = 'bogus-swapfile'
execute('recover '..swapname) -- This should not segfault. #2117
eq('E305: No swap file found for '..swapname, eval('v:errmsg'))
end)
end)
describe(':preserve', function()
local swapdir = lfs.currentdir()..'/testdir_recover_spec'
before_each(function()
clear()
os.remove(swapdir)
lfs.mkdir(swapdir)
end)
after_each(function()
os.remove(swapdir)
end)
it("saves to custom 'directory' and (R)ecovers (issue #1836)", function()
local testfile = 'testfile_recover_spec'
local init = [[
set swapfile fileformat=unix undolevels=-1
set directory^=]]..swapdir..[[//
]]
source(init)
execute('set swapfile fileformat=unix undolevels=-1')
-- Put swapdir at the start of the 'directory' list. #1836
execute('set directory^='..swapdir..'//')
execute('edit '..testfile)
feed('isometext<esc>')
execute('preserve')
source('redir => g:swapname | swapname | redir END')
local swappath1 = eval('g:swapname')
--TODO(justinmk): this is an ugly hack to force `helpers` to support
--multiple sessions.
local nvim2 = helpers.spawn({helpers.nvim_prog, '-u', 'NONE', '--embed'})
helpers.set_session(nvim2)
source(init)
-- Use the "SwapExists" event to choose the (R)ecover choice at the dialog.
execute('autocmd SwapExists * let v:swapchoice = "r"')
execute('silent edit '..testfile)
source('redir => g:swapname | swapname | redir END')
local swappath2 = eval('g:swapname')
-- swapfile from session 1 should end in .swp
assert(testfile..'.swp' == string.match(swappath1, '[^%%]+$'))
-- swapfile from session 2 should end in .swo
assert(testfile..'.swo' == string.match(swappath2, '[^%%]+$'))
expect('sometext')
end)
end)

View File

@ -18,6 +18,10 @@ if nvim_dir == nvim_prog then
nvim_dir = "." nvim_dir = "."
end end
-- Nvim "Unit Under Test" http://en.wikipedia.org/wiki/Device_under_test
local NvimUUT = {}
NvimUUT.__index = NvimUUT
local prepend_argv local prepend_argv
if os.getenv('VALGRIND') then if os.getenv('VALGRIND') then
@ -49,6 +53,10 @@ end
local session, loop_running, loop_stopped, last_error local session, loop_running, loop_stopped, last_error
local function set_session(s)
session = s
end
local function request(method, ...) local function request(method, ...)
local status, rv = session:request(method, ...) local status, rv = session:request(method, ...)
if not status then if not status then
@ -305,5 +313,6 @@ return {
curwin = curwin, curwin = curwin,
curtab = curtab, curtab = curtab,
curbuf_contents = curbuf_contents, curbuf_contents = curbuf_contents,
wait = wait wait = wait,
set_session = set_session
} }

View File

@ -0,0 +1,80 @@
-- Inserts 10000 lines with text to fill the swap file with two levels of
-- pointer blocks. Then recovers from the swap file and checks all text is
-- restored. We need about 10000 lines of 100 characters to get two levels of
-- pointer blocks.
local helpers = require('test.functional.helpers')
local feed, insert, source = helpers.feed, helpers.insert, helpers.source
local clear, execute, expect, source = helpers.clear, helpers.execute, helpers.expect, helpers.source
local eval = helpers.eval
describe('78', function()
setup(clear)
teardown(function()
os.remove(".Xtest.swp")
os.remove(".Xtest.swo")
end)
it('is working', function()
source([=[
set swapfile fileformat=unix undolevels=-1
e! Xtest
let text = "\tabcdefghijklmnoparstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnoparstuvwxyz0123456789"
let i = 1
let linecount = 10000
while i <= linecount | call append(i - 1, i . text) | let i += 1 | endwhile
preserve
" Get the name of the swap file, and clean up the :redir capture.
redir => g:swapname | swapname | redir END
let g:swapname = substitute(g:swapname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', 'g')
let g:swapname = fnameescape(g:swapname)
" Make a copy of the swap file in Xswap
set bin
exe 'sp ' . g:swapname
w! Xswap
set nobin
new
only!
bwipe! Xtest
call rename('Xswap', g:swapname)
"TODO(jkeyes): without 'silent', this hangs the test " at message:
" 'Recovery completed. You should check if everything is OK.'
silent recover Xtest
call delete(g:swapname)
new
call append(0, 'recovery start')
wincmd w
let g:linedollar = line('$')
if g:linedollar < linecount
wincmd w
call append(line('$'), "expected " . linecount
\ . " lines but found only " . g:linedollar)
wincmd w
let linecount = g:linedollar
endif
let i = 1
while i <= linecount
if getline(i) != i . text
exe 'wincmd w'
call append(line('$'), i . ' differs')
exe 'wincmd w'
endif
let i += 1
endwhile
q!
call append(line('$'), 'recovery end')
]=])
expect([[
recovery start
recovery end]])
end)
end)