Patch from vodz:
[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 permisions on 'path', pass the appropriate 'mode'
35  * val.  Otherwise, pass -1 to get default permisions.
36  */
37
38 #include <errno.h>
39 #include <unistd.h>
40 #include "libbb.h"
41
42 int bb_make_directory (char *path, long mode, int flags)
43 {
44         mode_t mask;
45         const char *fail_msg;
46         char *s = path;
47         char c;
48
49         mask = umask(0);
50         umask(mask & ~0300);
51
52         do {
53                 c = 0;
54
55                 if (flags & FILEUTILS_RECUR) {  /* Get the parent. */
56                         /* Bypass leading non-'/'s and then subsequent '/'s. */
57                         while (*s) {
58                                 if (*s == '/') {
59                                         do {
60                                                 ++s;
61                                         } while (*s == '/');
62                                         c = *s;         /* Save the current char */
63                                         *s = 0;         /* and replace it with nul. */
64                                         break;
65                                 }
66                                 ++s;
67                         }
68                 }
69
70                 if (mkdir(path, 0777) < 0) {
71                         /* If we failed for any other reason than the directory
72                          * already exists, output a diagnostic and return -1.*/
73                         if (errno != EEXIST) {
74                                 fail_msg = "create";
75                                 umask(mask);
76                                 break;
77                         }
78                         /* Since the directory exists, don't attempt to change
79                          * permissions if it was the full target.  Note that
80                          * this is not an error conditon. */
81                         if (!c) {
82                                 umask(mask);
83                                 return 0;
84                         }
85                 }
86
87                 if (!c) {
88                         /* Done.  If necessary, updated perms on the newly
89                          * created directory.  Failure to update here _is_
90                          * an error.*/
91                         umask(mask);
92                         if ((mode != -1) && (chmod(path, mode) < 0)){
93                                 fail_msg = "set permissions of";
94                                 break;
95                         }
96                         return 0;
97                 }
98
99                 /* Remove any inserted nul from the path (recursive mode). */
100                 *s = c;
101
102         } while (1);
103
104         bb_perror_msg ("Cannot %s directory `%s'", fail_msg, path);
105         return -1;
106 }