Patch from David Daney:
[oweals/busybox.git] / libbb / make_directory.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * parse_mode implementation for busybox
4  *
5  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  */
22
23 /* Mar 5, 2003    Manuel Novoa III
24  *
25  * This is the main work function for the 'mkdir' applet.  As such, it
26  * strives to be SUSv3 compliant in it's behaviour when recursively
27  * making missing parent dirs, and in it's mode setting of the final
28  * directory 'path'.
29  *
30  * To recursively build all missing intermediate directories, make
31  * sure that (flags & FILEUTILS_RECUR) is non-zero.  Newly created
32  * intermediate directories will have at least u+wx perms.
33  *
34  * To set specific permissions on 'path', pass the appropriate 'mode'
35  * val.  Otherwise, pass -1 to get default permissions.
36  */
37
38 #include <errno.h>
39 #include <unistd.h>
40 #include <sys/stat.h>
41 #include "libbb.h"
42
43 int bb_make_directory (char *path, long mode, int flags)
44 {
45         mode_t mask;
46         const char *fail_msg;
47         char *s = path;
48         char c;
49         struct stat st;
50
51         mask = umask(0);
52         umask(mask & ~0300);
53
54         do {
55                 c = 0;
56
57                 if (flags & FILEUTILS_RECUR) {  /* Get the parent. */
58                         /* Bypass leading non-'/'s and then subsequent '/'s. */
59                         while (*s) {
60                                 if (*s == '/') {
61                                         do {
62                                                 ++s;
63                                         } while (*s == '/');
64                                         c = *s;         /* Save the current char */
65                                         *s = 0;         /* and replace it with nul. */
66                                         break;
67                                 }
68                                 ++s;
69                         }
70                 }
71
72                 if (mkdir(path, 0777) < 0) {
73                         /* If we failed for any other reason than the directory
74                          * already exists, output a diagnostic and return -1.*/
75                         if (errno != EEXIST
76                                 || !(flags & FILEUTILS_RECUR)
77                                 || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
78                                 fail_msg = "create";
79                                 umask(mask);
80                                 break;
81                         }
82                         /* Since the directory exists, don't attempt to change
83                          * permissions if it was the full target.  Note that
84                          * this is not an error conditon. */
85                         if (!c) {
86                                 umask(mask);
87                                 return 0;
88                         }
89                 }
90
91                 if (!c) {
92                         /* Done.  If necessary, updated perms on the newly
93                          * created directory.  Failure to update here _is_
94                          * an error.*/
95                         umask(mask);
96                         if ((mode != -1) && (chmod(path, mode) < 0)){
97                                 fail_msg = "set permissions of";
98                                 break;
99                         }
100                         return 0;
101                 }
102
103                 /* Remove any inserted nul from the path (recursive mode). */
104                 *s = c;
105
106         } while (1);
107
108         bb_perror_msg ("Cannot %s directory `%s'", fail_msg, path);
109         return -1;
110 }