From b8a5b08de163e80e1b72459c5c889bb70eef947b Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Sat, 8 Feb 2020 07:50:07 -0800 Subject: 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. --- sysif.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'sysif.c') 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; } { -- cgit v1.2.3