Re: user/5141: race in mkdir -p may cause wrong successful exit status

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Re: user/5141: race in mkdir -p may cause wrong successful exit status

Bruno Carnazzi
The following reply was made to PR user/5141; it has been noted by GNATS.

From: "Bruno Carnazzi" <[hidden email]>
To: [hidden email]
Cc:  
Subject: Re: user/5141: race in mkdir -p may cause wrong successful exit status
Date: Mon, 7 Aug 2006 22:07:01 +0400

 >Fix:
         Here's a patch largely inspired by NetBSD mkdir()s implementation :
 
 --- mkdir.c.orig        Sat Aug  5 07:52:26 2006
 +++ mkdir.c     Sun Aug  6 23:37:39 2006
 @@ -154,17 +154,37 @@
                done = (*slash == '\0');
                *slash = '\0';
 
 -               if (stat(path, &sb)) {
 -                       if (errno != ENOENT ||
 -                           (mkdir(path, done ? mode : dir_mode) &&
 -                           errno != EEXIST)) {
 -                               warn("%s", path);
 -                               return (-1);
 -                       }
 -               } else if (!S_ISDIR(sb.st_mode)) {
 -                       warnx("%s: %s", path, strerror(ENOTDIR));
 -                       return (-1);
 -               }
 +        if (mkdir(path, done ? mode : dir_mode) < 0) {
 +            int mkdir_errno = errno;
 +
 +            if (stat(path, &sb)) {
 +                /* Not there; use mkdir()s errno */
 +                errno = mkdir_errno;
 +                warn("%s", path);
 +                return (-1);
 +            }
 +            if (!S_ISDIR(sb.st_mode)) {
 +                /* Is there, but isn't a directory */
 +                errno = ENOTDIR;
 +                warn("%s", path);
 +                return (-1);
 +            }
 +        }
 +
 +        if (done) {
 +            /*
 +             * The mkdir() and umask() calls both honor only the low
 +             * nine bits, so if you try to set a mode including the
 +             * sticky, setuid, setgid bits you lose them.  Don't do
 +             * this unless the user has specifically requested a mode
 +             * as chmod will (obviously) ignore the umask.
 +             */
 +            if (mode > 0777 && chmod(path, mode) == -1) {
 +                warn("%s", path);
 +                return (-1);
 +            }
 +            break;
 +        }
 
                *slash = '/';
        }