@@ -1825,7 +1825,6 @@ write_viminfo(char_u *file, int forceit)
18251825 FILE * fp_out = NULL ; /* output viminfo file */
18261826 char_u * tempname = NULL ; /* name of temp viminfo file */
18271827 stat_T st_new ; /* mch_stat() of potential new file */
1828- char_u * wp ;
18291828#if defined(UNIX ) || defined(VMS )
18301829 mode_t umask_save ;
18311830#endif
@@ -1847,27 +1846,29 @@ write_viminfo(char_u *file, int forceit)
18471846 fp_in = mch_fopen ((char * )fname , READBIN );
18481847 if (fp_in == NULL )
18491848 {
1849+ int fd ;
1850+
18501851 /* if it does exist, but we can't read it, don't try writing */
18511852 if (mch_stat ((char * )fname , & st_new ) == 0 )
18521853 goto end ;
1853- #if defined(UNIX ) || defined(VMS )
1854- /*
1855- * For Unix we create the .viminfo non-accessible for others,
1856- * because it may contain text from non-accessible documents.
1857- */
1858- umask_save = umask (077 );
1859- #endif
1860- fp_out = mch_fopen ((char * )fname , WRITEBIN );
1861- #if defined(UNIX ) || defined(VMS )
1862- (void )umask (umask_save );
1863- #endif
1854+
1855+ /* Create the new .viminfo non-accessible for others, because it may
1856+ * contain text from non-accessible documents. It is up to the user to
1857+ * widen access (e.g. to a group). This may also fail if there is a
1858+ * race condition, then just give up. */
1859+ fd = mch_open ((char * )fname ,
1860+ O_CREAT |O_EXTRA |O_EXCL |O_WRONLY |O_NOFOLLOW , 0600 );
1861+ if (fd < 0 )
1862+ goto end ;
1863+ fp_out = fdopen (fd , WRITEBIN );
18641864 }
18651865 else
18661866 {
18671867 /*
18681868 * There is an existing viminfo file. Create a temporary file to
18691869 * write the new viminfo into, in the same directory as the
1870- * existing viminfo file, which will be renamed later.
1870+ * existing viminfo file, which will be renamed once all writing is
1871+ * successful.
18711872 */
18721873#ifdef UNIX
18731874 /*
@@ -1901,12 +1902,18 @@ write_viminfo(char_u *file, int forceit)
19011902#endif
19021903
19031904 /*
1904- * Make tempname.
1905+ * Make tempname, find one that does not exist yet.
1906+ * Beware of a race condition: If someone logs out and all Vim
1907+ * instances exit at the same time a temp file might be created between
1908+ * stat() and open(). Use mch_open() with O_EXCL to avoid that.
19051909 * May try twice: Once normal and once with shortname set, just in
19061910 * case somebody puts his viminfo file in an 8.3 filesystem.
19071911 */
19081912 for (;;)
19091913 {
1914+ int next_char = 'z' ;
1915+ char_u * wp ;
1916+
19101917 tempname = buf_modname (
19111918#ifdef UNIX
19121919 shortname ,
@@ -1924,127 +1931,129 @@ write_viminfo(char_u *file, int forceit)
19241931 break ;
19251932
19261933 /*
1927- * Check if tempfile already exists. Never overwrite an
1928- * existing file!
1934+ * Try a series of names. Change one character, just before
1935+ * the extension. This should also work for an 8.3
1936+ * file name, when after adding the extension it still is
1937+ * the same file as the original.
19291938 */
1930- if (mch_stat ((char * )tempname , & st_new ) == 0 )
1939+ wp = tempname + STRLEN (tempname ) - 5 ;
1940+ if (wp < gettail (tempname )) /* empty file name? */
1941+ wp = gettail (tempname );
1942+ for (;;)
19311943 {
1932- #ifdef UNIX
1933- /*
1934- * Check if tempfile is same as original file. May happen
1935- * when modname() gave the same file back. E.g. silly
1936- * link, or file name-length reached. Try again with
1937- * shortname set.
1938- */
1939- if (!shortname && st_new .st_dev == st_old .st_dev
1940- && st_new .st_ino == st_old .st_ino )
1941- {
1942- vim_free (tempname );
1943- tempname = NULL ;
1944- shortname = TRUE;
1945- continue ;
1946- }
1947- #endif
19481944 /*
1949- * Try another name. Change one character, just before
1950- * the extension. This should also work for an 8.3
1951- * file name, when after adding the extension it still is
1952- * the same file as the original.
1945+ * Check if tempfile already exists. Never overwrite an
1946+ * existing file!
19531947 */
1954- wp = tempname + STRLEN (tempname ) - 5 ;
1955- if (wp < gettail (tempname )) /* empty file name? */
1956- wp = gettail (tempname );
1957- for (* wp = 'z' ; mch_stat ((char * )tempname , & st_new ) == 0 ;
1958- -- * wp )
1948+ if (mch_stat ((char * )tempname , & st_new ) == 0 )
19591949 {
1950+ #ifdef UNIX
19601951 /*
1961- * They all exist? Must be something wrong! Don't
1962- * write the viminfo file then.
1952+ * Check if tempfile is same as original file. May happen
1953+ * when modname() gave the same file back. E.g. silly
1954+ * link, or file name-length reached. Try again with
1955+ * shortname set.
19631956 */
1964- if (* wp == 'a' )
1957+ if (!shortname && st_new .st_dev == st_old .st_dev
1958+ && st_new .st_ino == st_old .st_ino )
19651959 {
1966- EMSG2 (_ ("E929: Too many viminfo temp files, like %s!" ),
1967- tempname );
19681960 vim_free (tempname );
19691961 tempname = NULL ;
1962+ shortname = TRUE;
19701963 break ;
19711964 }
1965+ #endif
19721966 }
1973- }
1974- break ;
1975- }
1976-
1977- if (tempname != NULL )
1978- {
1967+ else
1968+ {
1969+ /* Try creating the file exclusively. This may fail if
1970+ * another Vim tries to do it at the same time. */
19791971#ifdef VMS
1980- /* fdopen() fails for some reason */
1981- umask_save = umask (077 );
1982- fp_out = mch_fopen ((char * )tempname , WRITEBIN );
1983- (void )umask (umask_save );
1972+ /* fdopen() fails for some reason */
1973+ umask_save = umask (077 );
1974+ fp_out = mch_fopen ((char * )tempname , WRITEBIN );
1975+ (void )umask (umask_save );
19841976#else
1985- int fd ;
1977+ int fd ;
19861978
1987- /* Use mch_open() to be able to use O_NOFOLLOW and set file
1988- * protection:
1989- * Unix: same as original file, but strip s-bit. Reset umask to
1990- * avoid it getting in the way.
1991- * Others: r&w for user only. */
1979+ /* Use mch_open() to be able to use O_NOFOLLOW and set file
1980+ * protection:
1981+ * Unix: same as original file, but strip s-bit. Reset
1982+ * umask to avoid it getting in the way.
1983+ * Others: r&w for user only. */
19921984# ifdef UNIX
1993- umask_save = umask (0 );
1994- fd = mch_open ((char * )tempname ,
1995- O_CREAT |O_EXTRA |O_EXCL |O_WRONLY |O_NOFOLLOW ,
1996- (int )((st_old .st_mode & 0777 ) | 0600 ));
1997- (void )umask (umask_save );
1985+ umask_save = umask (0 );
1986+ fd = mch_open ((char * )tempname ,
1987+ O_CREAT |O_EXTRA |O_EXCL |O_WRONLY |O_NOFOLLOW ,
1988+ (int )((st_old .st_mode & 0777 ) | 0600 ));
1989+ (void )umask (umask_save );
19981990# else
1999- fd = mch_open ((char * )tempname ,
2000- O_CREAT |O_EXTRA |O_EXCL |O_WRONLY |O_NOFOLLOW , 0600 );
1991+ fd = mch_open ((char * )tempname ,
1992+ O_CREAT |O_EXTRA |O_EXCL |O_WRONLY |O_NOFOLLOW , 0600 );
20011993# endif
2002- if (fd < 0 )
2003- fp_out = NULL ;
2004- else
2005- fp_out = fdopen (fd , WRITEBIN );
1994+ if (fd < 0 )
1995+ {
1996+ fp_out = NULL ;
1997+ # ifdef EEXIST
1998+ /* Avoid trying lots of names while the problem is lack
1999+ * of premission, only retry if the file already
2000+ * exists. */
2001+ if (errno != EEXIST )
2002+ break ;
2003+ # endif
2004+ }
2005+ else
2006+ fp_out = fdopen (fd , WRITEBIN );
20062007#endif /* VMS */
2008+ if (fp_out != NULL )
2009+ break ;
2010+ }
20072011
2008- /*
2009- * If we can't create in the same directory, try creating a
2010- * "normal" temp file. This is just an attempt, renaming the temp
2011- * file might fail as well.
2012- */
2013- if (fp_out == NULL )
2014- {
2015- vim_free (tempname );
2016- if ((tempname = vim_tempname ('o' , TRUE)) != NULL )
2017- fp_out = mch_fopen ((char * )tempname , WRITEBIN );
2012+ /* Assume file exists, try again with another name. */
2013+ if (next_char == 'a' - 1 )
2014+ {
2015+ /* They all exist? Must be something wrong! Don't write
2016+ * the viminfo file then. */
2017+ EMSG2 (_ ("E929: Too many viminfo temp files, like %s!" ),
2018+ tempname );
2019+ break ;
2020+ }
2021+ * wp = next_char ;
2022+ -- next_char ;
20182023 }
20192024
2025+ if (tempname != NULL )
2026+ break ;
2027+ /* continue if shortname was set */
2028+ }
2029+
20202030#if defined(UNIX ) && defined(HAVE_FCHOWN )
2031+ if (tempname != NULL && fp_out != NULL )
2032+ {
2033+ stat_T tmp_st ;
2034+
20212035 /*
20222036 * Make sure the original owner can read/write the tempfile and
20232037 * otherwise preserve permissions, making sure the group matches.
20242038 */
2025- if (fp_out != NULL )
2039+ if (mch_stat (( char * ) tempname , & tmp_st ) >= 0 )
20262040 {
2027- stat_T tmp_st ;
2028-
2029- if (mch_stat ((char * )tempname , & tmp_st ) >= 0 )
2030- {
2031- if (st_old .st_uid != tmp_st .st_uid )
2032- /* Changing the owner might fail, in which case the
2033- * file will now owned by the current user, oh well. */
2034- ignored = fchown (fileno (fp_out ), st_old .st_uid , -1 );
2035- if (st_old .st_gid != tmp_st .st_gid
2036- && fchown (fileno (fp_out ), -1 , st_old .st_gid ) == -1 )
2037- /* can't set the group to what it should be, remove
2038- * group permissions */
2039- (void )mch_setperm (tempname , 0600 );
2040- }
2041- else
2042- /* can't stat the file, set conservative permissions */
2041+ if (st_old .st_uid != tmp_st .st_uid )
2042+ /* Changing the owner might fail, in which case the
2043+ * file will now owned by the current user, oh well. */
2044+ ignored = fchown (fileno (fp_out ), st_old .st_uid , -1 );
2045+ if (st_old .st_gid != tmp_st .st_gid
2046+ && fchown (fileno (fp_out ), -1 , st_old .st_gid ) == -1 )
2047+ /* can't set the group to what it should be, remove
2048+ * group permissions */
20432049 (void )mch_setperm (tempname , 0600 );
20442050 }
2045- #endif
2051+ else
2052+ /* can't stat the file, set conservative permissions */
2053+ (void )mch_setperm (tempname , 0600 );
20462054 }
20472055 }
2056+ #endif
20482057
20492058 /*
20502059 * Check if the new viminfo file can be written to.
0 commit comments