Skip to content

Commit cd142e3

Browse files
committed
patch 8.0.1300: file permissions may end up wrong when writing
Problem: File permissions may end up wrong when writing. Solution: Use fchmod() instead of chmod() when possible. Don't truncate until we know we can change the file.
1 parent a42ad57 commit cd142e3

7 files changed

Lines changed: 88 additions & 24 deletions

File tree

src/auto/configure

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12090,13 +12090,13 @@ if test "x$vim_cv_getcwd_broken" = "xyes" ; then
1209012090

1209112091
fi
1209212092

12093-
for ac_func in fchdir fchown fsync getcwd getpseudotty \
12093+
for ac_func in fchdir fchown fchmod fsync getcwd getpseudotty \
1209412094
getpwent getpwnam getpwuid getrlimit gettimeofday getwd lstat \
1209512095
memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \
1209612096
getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \
1209712097
sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \
1209812098
strnicmp strpbrk strtol tgetent towlower towupper iswupper \
12099-
usleep utime utimes mblen
12099+
usleep utime utimes mblen ftruncate
1210012100
do :
1210112101
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1210212102
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"

src/config.h.in

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,11 @@
156156
/* Define if you the function: */
157157
#undef HAVE_FCHDIR
158158
#undef HAVE_FCHOWN
159+
#undef HAVE_FCHMOD
160+
#undef HAVE_FLOAT_FUNCS
159161
#undef HAVE_FSEEKO
160162
#undef HAVE_FSYNC
161-
#undef HAVE_FLOAT_FUNCS
163+
#undef HAVE_FTRUNCATE
162164
#undef HAVE_GETCWD
163165
#undef HAVE_GETPGID
164166
#undef HAVE_GETPSEUDOTTY

src/configure.ac

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3655,13 +3655,13 @@ fi
36553655

36563656
dnl Check for functions in one big call, to reduce the size of configure.
36573657
dnl Can only be used for functions that do not require any include.
3658-
AC_CHECK_FUNCS(fchdir fchown fsync getcwd getpseudotty \
3658+
AC_CHECK_FUNCS(fchdir fchown fchmod fsync getcwd getpseudotty \
36593659
getpwent getpwnam getpwuid getrlimit gettimeofday getwd lstat \
36603660
memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \
36613661
getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \
36623662
sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \
36633663
strnicmp strpbrk strtol tgetent towlower towupper iswupper \
3664-
usleep utime utimes mblen)
3664+
usleep utime utimes mblen ftruncate)
36653665
AC_FUNC_FSEEKO
36663666

36673667
dnl define _LARGE_FILES, _FILE_OFFSET_BITS and _LARGEFILE_SOURCE when

src/fileio.c

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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
{
@@ -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

src/os_unix.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,9 +2725,8 @@ mch_getperm(char_u *name)
27252725
}
27262726

27272727
/*
2728-
* set file permission for 'name' to 'perm'
2729-
*
2730-
* return FAIL for failure, OK otherwise
2728+
* Set file permission for "name" to "perm".
2729+
* Return FAIL for failure, OK otherwise.
27312730
*/
27322731
int
27332732
mch_setperm(char_u *name, long perm)
@@ -2741,6 +2740,18 @@ mch_setperm(char_u *name, long perm)
27412740
(mode_t)perm) == 0 ? OK : FAIL);
27422741
}
27432742

2743+
#if defined(HAVE_FCHMOD) || defined(PROTO)
2744+
/*
2745+
* Set file permission for open file "fd" to "perm".
2746+
* Return FAIL for failure, OK otherwise.
2747+
*/
2748+
int
2749+
mch_fsetperm(int fd, long perm)
2750+
{
2751+
return (fchmod(fd, (mode_t)perm) == 0 ? OK : FAIL);
2752+
}
2753+
#endif
2754+
27442755
#if defined(HAVE_ACL) || defined(PROTO)
27452756
# ifdef HAVE_SYS_ACL_H
27462757
# include <sys/acl.h>

src/proto/os_unix.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ int mch_isFullName(char_u *fname);
3636
void fname_case(char_u *name, int len);
3737
long mch_getperm(char_u *name);
3838
int mch_setperm(char_u *name, long perm);
39+
int mch_fsetperm(int fd, long perm);
3940
void mch_copy_sec(char_u *from_file, char_u *to_file);
4041
vim_acl_T mch_get_acl(char_u *fname);
4142
void mch_set_acl(char_u *fname, vim_acl_T aclent);

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,8 @@ static char *(features[]) =
766766

767767
static int included_patches[] =
768768
{ /* Add new patch number below this line */
769+
/**/
770+
1300,
769771
/**/
770772
1299,
771773
/**/

0 commit comments

Comments
 (0)