[Toybox] Fixes to mkdir, chown, env

Rob Landley rob at landley.net
Thu Sep 5 02:29:20 PDT 2013


On 08/30/2013 09:01:30 AM, Jacek Bukarewicz wrote:
> Hi
> 
> While testing toybox I encountered a few minor issues with toybox:
> 1) mkdir:
>     - according to the spec umask should be ignored when explicitly  
> setting permissions. I simply added umask(0) call to prevent umask  
> from affecting mode

There's a TOYBOX_UMASK flag you can add to newtoy that sets umask(0)  
before the command's main gets called. Alas, didn't turn out to be  
quite as universal as I thought because a lot of stuff wants to  
_selectively_ disable it, but it might apply here.

mkdir was implemented before we had the permission parsing logic in the  
library, so -m was added later and apparently never fully cleaned up.  
(hg log toys/mkdir.c)

>      - I believe that in mkdir.c line 60 the intent was to check if  
> flag "p" is not set.

save is initialized to 0 on line 46, and only set to *s on line 50  
inside
an if (toys.optflags&FLAG_p). Therefore the assignment at the end will
always assign 0 in the non-p case, so we don't explicitly need to test  
for
FLAG_p because it's implicit in the variable state.

(I try to come up with a test to demonstrate the failing behavior when  
dealing with code like that. Then I can show my change fixed the test  
case.)

>      - I added --mode longopt, but I noticed that it's not handled  
> correctly.
> I believe that's because gof.arg is assigned to "" (get_optflags  
> function in
> lib/args.c). gof.arg is incremented afterwards so it contains trash  
> then.
> I removed this line from lib/args.c and it works fine now

The problem is removing that line breaks other things. (There's both
"--mode=arg" and "--mode arg" and the short opt "-m arg" and "-marg"  
and of course the tar or ps style "tar xvjfC filename dirname" where C  
is not the option to f...

I redid this area recently due to Rich Felker asking for --color=auto  
to behave differently from --color with no argument, and I had a  
pending todo item that bare longopts with arguments weren't working  
right anyway, so...

   http://landley.net/hg/toybox/rev/1036

Hopefully it works now.

>     - I also added/modified a few mkdir  tests

Cool.

What's this bit?

@@ -42,19 +42,22 @@ static int do_mkdir(char *dir)
      return 1;
    }

-  for (s=dir; ; s++) {
+  // Skip leading / of absolute paths.
+  for (s=dir+1; ; s++) {
      char save=0;

-    // Skip leading / of absolute paths.
-    if (s!=dir && *s == '/' && (toys.optflags&FLAG_p)) {
+    if (*s == '/' && (toys.optflags&FLAG_p)) {

Is that forbidden?

   landley at driftwood:~/toybox/toybox$ mkdir -p /home/landley/walrosity
   landley at driftwood:~/toybox/toybox$ ls -ld /home/landley/walrosity
   drwxrwxr-x 2 landley landley 4096 Sep  4 04:45  
/home/landley/walrosity

Absolute paths work fine in the host version of this tool...? Standard  
filesystem permissions should prevent it from doing anything funky. The  
posix mkdir page doesn't contain the word "absolute'...

And this:

-    if ((toys.optflags&FLAG_m) && save != '/') mode = TT.mode;
+    if ((toys.optflags&FLAG_m) && save != '/') {
+      umask(0); // ignore umask for last path component
+      mode = TT.mode;
+    }

No, that's too simple. Posix says:

   -m  mode
     Set the file permission bits of the newly-created directory to the
     specified mode value. The mode option-argument shall be the same as
     the mode operand defined for the chmod utility. In the  
symbolic_mode
     strings, the op characters '+' and '-' shall be interpreted  
relative
     to an assumed initial mode of a= rwx; '+' shall add permissions to
     the default mode, '-' shall delete permissions from the default  
mode.

   -p
     Create any missing intermediate pathname components.
     For each dir operand that does not name an existing directory,  
effects
     equivalent to those caused by the following command shall occur:

     mkdir -p -m $(umask -S),u+wx $(dirname dir) &&
     mkdir [-m mode] dir

     where the -m mode option represents that option supplied to the
     original invocation of mkdir, if any.

Sigh. I'll take a swing at fixing this, but if the host umask is  
switching
off u+wx our behavior would still be wrong. (And I need a test for  
that.)

Heh, this code also predates teh automatic generation of FLAG_x macros,  
so
it was using hardwired constants and it looks like the conversion gave  
us
"toys.optflags&~FLAG_p" which is just another way of saying
toys.optflags&FLAG_m. Now the question is, SHOULD it be saying that?
FLAG_p allows failure, so I think it should be saying !(flag&FLAG_p) I
guess? (Rummage, rummage: ah, I didn't write commit 763, which added  
this.
Ok, that would explain why I don't remember doing something subtle here.
Not that I caught it before now, either... :)

And the existing mkdir tests are failing. Why...?

   VERBOSE=1 scripts/test.sh mkdir

Becuase we've implemented stat and are no longer using the host's  
version, and our stat -a is saying 0775 where the host one was saying  
775.

Sigh. And of course there's no spec for stat. Ok, try it on gentoo,  
ubuntu 8.04, and Red Hat 9 from a decade ago... not padding. We're  
padding to 4 digits, and should stop that.

Huh. The last test is failing, but passing for TEST_HOST=1. Why is the  
last test failing? Directory permissions are wrong. Why are permissions  
wrong? It's creating them right from the command line...

Because the rm -rf one just before that isn't deleting anything when  
using the toybox rm, but is when using the TEST_HOST rm. Not a mkdir  
issue, it's an rm issue!

It's weird because rm is returning 0 (no error) even though it didn't  
delete anything. And strace says it's traversing but not calling  
unlink. Why is not it not trying to delete? Because try->symlink is 1  
in directory entries which means nodelete flagged its parent (so we  
don't complain about being able to delete parent directories we  
couldn't remove the contents of), but how did that get flagged?

Ah, the dirtree infrastructure _also_ sets the parent's symlink=1 to  
specify child had an error (symlink doesn't have a valid nonzero value  
for directory entries), and the permissions of "124" don't let us  
enumerate the contents of the directory "two", so when it tries to stat  
the "." and ".." entries in there it flags it as having had an error:

   $ mkdir sub
   $ chmod 007 sub
   $ ls -l sub
   ls: cannot open directory sub: Permission denied

(Yeah, funky, but that's how linux does permissions. Permissions 077  
says anybody completely unrelated to us can read the file, but we  
can't...)

So directory traversal flags an error which rm sees as a nerror that  
was already handled and it's just suppressing further errors, so the  
exit code doesn't get set because it never _reported_ an error.

Ok, what's the right thing to do here? According to strace the host rm  
isn't chmoding entries, it's just trying to delete the directory even  
though it couldn't read its contents, and I think that's probably  
correct. But I don't want it failing to report an _actual_ failure to  
delete the innermost thing, and making it attempt to delete even in the  
do not report error case opens the possiblity of that failing for the  
leaf node and then not getting reported. Um. Ok, set it to a  
_different_ flag value.

Rob
 1378373360.0


More information about the Toybox mailing list