@@ -3863,6 +3863,7 @@ buf_write(
38633863 char_u * rootname ;
38643864#if defined(UNIX )
38653865 int did_set_shortname ;
3866+ mode_t umask_save ;
38663867#endif
38673868
38683869 copybuf = alloc (BUFSIZE + 1 );
@@ -3994,22 +3995,30 @@ buf_write(
39943995 /* remove old backup, if present */
39953996 mch_remove (backup );
39963997 /* Open with O_EXCL to avoid the file being created while
3997- * we were sleeping (symlink hacker attack?) */
3998+ * we were sleeping (symlink hacker attack?). Reset umask
3999+ * if possible to avoid mch_setperm() below. */
4000+ #ifdef UNIX
4001+ umask_save = umask (0 );
4002+ #endif
39984003 bfd = mch_open ((char * )backup ,
39994004 O_WRONLY |O_CREAT |O_EXTRA |O_EXCL |O_NOFOLLOW ,
40004005 perm & 0777 );
4006+ #ifdef UNIX
4007+ (void )umask (umask_save );
4008+ #endif
40014009 if (bfd < 0 )
40024010 {
40034011 vim_free (backup );
40044012 backup = NULL ;
40054013 }
40064014 else
40074015 {
4008- /* set file protection same as original file, but
4009- * strip s-bit */
4016+ /* Set file protection same as original file, but
4017+ * strip s-bit. Only needed if umask() wasn't used
4018+ * above. */
4019+ #ifndef UNIX
40104020 (void )mch_setperm (backup , perm & 0777 );
4011-
4012- #ifdef UNIX
4021+ #else
40134022 /*
40144023 * Try to set the group of the backup same as the
40154024 * original file. If this fails, set the protection
@@ -4377,6 +4386,11 @@ buf_write(
43774386 }
43784387 else
43794388 {
4389+ #ifdef HAVE_FTRUNCATE
4390+ # define TRUNC_ON_OPEN 0
4391+ #else
4392+ # define TRUNC_ON_OPEN O_TRUNC
4393+ #endif
43804394 /*
43814395 * Open the file "wfname" for writing.
43824396 * We may try to open the file twice: If we can't write to the file
@@ -4389,7 +4403,7 @@ buf_write(
43894403 */
43904404 while ((fd = mch_open ((char * )wfname , O_WRONLY | O_EXTRA | (append
43914405 ? (forceit ? (O_APPEND | O_CREAT ) : O_APPEND )
4392- : (O_CREAT | O_TRUNC ))
4406+ : (O_CREAT | TRUNC_ON_OPEN ))
43934407 , perm < 0 ? 0666 : (perm & 0777 ))) < 0 )
43944408 {
43954409 /*
@@ -4482,6 +4496,30 @@ buf_write(
44824496 }
44834497 write_info .bw_fd = fd ;
44844498
4499+ #if defined(UNIX )
4500+ {
4501+ stat_T st ;
4502+
4503+ /* Double check we are writing the intended file before making
4504+ * any changes. */
4505+ if (overwriting
4506+ && (!dobackup || backup_copy )
4507+ && fname == wfname
4508+ && perm >= 0
4509+ && mch_fstat (fd , & st ) == 0
4510+ && st .st_ino != st_old .st_ino )
4511+ {
4512+ close (fd );
4513+ errmsg = (char_u * )_ ("E949: File changed while writing" );
4514+ goto fail ;
4515+ }
4516+ }
4517+ #endif
4518+ #ifdef HAVE_FTRUNCATE
4519+ if (!append )
4520+ ftruncate (fd , (off_t )0 );
4521+ #endif
4522+
44854523#if defined(WIN3264 )
44864524 if (backup != NULL && overwriting && !append )
44874525 {
@@ -4733,7 +4771,7 @@ buf_write(
47334771 */
47344772 if (p_fs && fsync (fd ) != 0 && !device )
47354773 {
4736- errmsg = (char_u * )_ ("E667: Fsync failed" );
4774+ errmsg = (char_u * )_ (e_fsync );
47374775 end = 0 ;
47384776 }
47394777#endif
@@ -4752,15 +4790,17 @@ buf_write(
47524790# ifdef HAVE_FCHOWN
47534791 stat_T st ;
47544792
4755- /* don 't change the owner when it's already OK, some systems remove
4756- * permission or ACL stuff */
4793+ /* Don 't change the owner when it's already OK, some systems remove
4794+ * permission or ACL stuff. */
47574795 if (mch_stat ((char * )wfname , & st ) < 0
47584796 || st .st_uid != st_old .st_uid
47594797 || st .st_gid != st_old .st_gid )
47604798 {
4761- ignored = fchown (fd , st_old .st_uid , st_old .st_gid );
4762- if (perm >= 0 ) /* set permission again, may have changed */
4763- (void )mch_setperm (wfname , perm );
4799+ /* changing owner might not be possible */
4800+ ignored = fchown (fd , st_old .st_uid , -1 );
4801+ /* if changing group fails clear the group permissions */
4802+ if (fchown (fd , -1 , st_old .st_gid ) == -1 && perm > 0 )
4803+ perm &= ~070 ;
47644804 }
47654805# endif
47664806 buf_setino (buf );
@@ -4770,18 +4810,26 @@ buf_write(
47704810 buf_setino (buf );
47714811#endif
47724812
4813+ #ifdef UNIX
4814+ if (made_writable )
4815+ perm &= ~0200 ; /* reset 'w' bit for security reasons */
4816+ #endif
4817+ #ifdef HAVE_FCHMOD
4818+ /* set permission of new file same as old file */
4819+ if (perm >= 0 )
4820+ (void )mch_fsetperm (fd , perm );
4821+ #endif
47734822 if (close (fd ) != 0 )
47744823 {
47754824 errmsg = (char_u * )_ ("E512: Close failed" );
47764825 end = 0 ;
47774826 }
47784827
4779- #ifdef UNIX
4780- if (made_writable )
4781- perm &= ~0200 ; /* reset 'w' bit for security reasons */
4782- #endif
4783- if (perm >= 0 ) /* set perm. of new file same as old file */
4828+ #ifndef HAVE_FCHMOD
4829+ /* set permission of new file same as old file */
4830+ if (perm >= 0 )
47844831 (void )mch_setperm (wfname , perm );
4832+ #endif
47854833#ifdef HAVE_ACL
47864834 /*
47874835 * Probably need to set the ACL before changing the user (can't set the
0 commit comments