summaryrefslogtreecommitdiffstats
path: root/sysif.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2020-02-08 07:50:07 -0800
committerKaz Kylheku <kaz@kylheku.com>2020-02-08 08:43:23 -0800
commitb8a5b08de163e80e1b72459c5c889bb70eef947b (patch)
treebabbb8db9933a0c509c25e39cd52d74b359a6af4 /sysif.c
parent158211060c413bcbdebba3f87478673cbee12dc0 (diff)
downloadtxr-b8a5b08de163e80e1b72459c5c889bb70eef947b.tar.gz
txr-b8a5b08de163e80e1b72459c5c889bb70eef947b.tar.bz2
txr-b8a5b08de163e80e1b72459c5c889bb70eef947b.zip
chmod: ugo must refer to unaltered perms.
Within the same clause, permissions given by ugo must refer to the unaltered permissions, before the target bits were masked out, otherwise self-assignment like o=o just clears the permissions. The other self-referential perm is X: it checks for existing x permissions. That works with the current value. * sysif.c (chmod_wrap): Keep the old permissions in a new loop variable called oldm. The u, g and o perms refer to oldm rather than to the updated value in cmode. When we hit a comma, we update oldm to the current value. The code for this is now in one place with a goto. * tests/018/chmod.tl: New test case that fails in the absence of this fix. Test cases confirming that X refers to the current permissions.
Diffstat (limited to 'sysif.c')
-rw-r--r--sysif.c16
1 files changed, 9 insertions, 7 deletions
diff --git a/sysif.c b/sysif.c
index 8778a242..444f8326 100644
--- a/sysif.c
+++ b/sysif.c
@@ -581,9 +581,9 @@ static val chmod_wrap(val target, val mode)
if (err == 0) {
const wchar_t *cm = c_str(mode);
wchar_t ch;
- mode_t srcm = 0;
+ mode_t srcm = 0, oldm = st.st_mode;
- cmode = st.st_mode;
+ cmode = oldm;
while ((ch = *cm++) != 0) {
switch (cs) {
@@ -612,9 +612,9 @@ static val chmod_wrap(val target, val mode)
perm:
case chm_perm:
switch (ch) {
- case 'u': srcm |= (cmode & 0700) >> 6; cs = chm_comma; break;
- case 'g': srcm |= (cmode & 0070) >> 3; cs = chm_comma; break;
- case 'o': srcm |= (cmode & 0007); cs = chm_comma; break;
+ case 'u': srcm |= (oldm & 0700) >> 6; cs = chm_comma; break;
+ case 'g': srcm |= (oldm & 0070) >> 3; cs = chm_comma; break;
+ case 'o': srcm |= (oldm & 0007); cs = chm_comma; break;
case 'r': srcm |= 4; cs = chm_nxtop; break;
case 'w': srcm |= 2; cs = chm_nxtop; break;
case 'x': srcm |= 1; cs = chm_nxtop; break;
@@ -622,7 +622,7 @@ static val chmod_wrap(val target, val mode)
case 't': srcm |= 020; cs = chm_nxtop; break;
case 'X': srcm |= ((cmode & 0111) != 0 ||
S_ISDIR(cmode)); cs = chm_nxtop; break;
- case ',': srcm = 0; who = 0; cs = chm_who; continue;
+ case ',': goto nextmode;
default:
goto inval;
}
@@ -630,7 +630,9 @@ static val chmod_wrap(val target, val mode)
case chm_comma:
if (ch != ',')
goto inval;
- srcm = 0; who = 0; cs = chm_who; continue;
+ nextmode:
+ srcm = 0; who = 0; oldm = cmode; cs = chm_who;
+ continue;
}
{