]>
Commit | Line | Data |
---|---|---|
b72432fd JA |
1 | /* mkdir - make directories */ |
2 | ||
3 | /* See Makefile for compilation details. */ | |
4 | ||
3185942a JA |
5 | /* |
6 | Copyright (C) 1999-2009 Free Software Foundation, Inc. | |
7 | ||
8 | This file is part of GNU Bash. | |
9 | Bash is free software: you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
11 | the Free Software Foundation, either version 3 of the License, or | |
12 | (at your option) any later version. | |
13 | ||
14 | Bash is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
21 | */ | |
22 | ||
b72432fd JA |
23 | #include <config.h> |
24 | ||
25 | #include "bashtypes.h" | |
26 | #include "posixstat.h" | |
27 | #include <errno.h> | |
28 | #include <stdio.h> | |
29 | #include "bashansi.h" | |
30 | #if defined (HAVE_UNISTD_H) | |
31 | # include <unistd.h> | |
32 | #endif | |
33 | ||
34 | #include "builtins.h" | |
35 | #include "shell.h" | |
36 | #include "bashgetopt.h" | |
3185942a | 37 | #include "common.h" |
b72432fd JA |
38 | |
39 | #if !defined (errno) | |
40 | extern int errno; | |
41 | #endif | |
42 | ||
43 | #define ISOCTAL(c) ((c) >= '0' && (c) <= '7') | |
44 | ||
45 | extern int parse_symbolic_mode (); | |
46 | ||
47 | static int make_path (); | |
48 | ||
49 | static int original_umask; | |
50 | ||
51 | int | |
52 | mkdir_builtin (list) | |
53 | WORD_LIST *list; | |
54 | { | |
a0c0a00f | 55 | int opt, pflag, omode, rval, nmode, parent_mode; |
b72432fd JA |
56 | char *mode; |
57 | WORD_LIST *l; | |
58 | ||
59 | reset_internal_getopt (); | |
60 | pflag = 0; | |
61 | mode = (char *)NULL; | |
62 | while ((opt = internal_getopt(list, "m:p")) != -1) | |
63 | switch (opt) | |
64 | { | |
65 | case 'p': | |
66 | pflag = 1; | |
67 | break; | |
68 | case 'm': | |
69 | mode = list_optarg; | |
70 | break; | |
71 | default: | |
72 | builtin_usage(); | |
73 | return (EX_USAGE); | |
74 | } | |
75 | list = loptend; | |
76 | ||
77 | if (list == 0) | |
78 | { | |
79 | builtin_usage (); | |
80 | return (EX_USAGE); | |
81 | } | |
82 | ||
83 | if (mode == NULL) | |
84 | omode = S_IRWXU | S_IRWXG | S_IRWXO; /* a=rwx */ | |
85 | else if (ISOCTAL (*mode)) /* octal number */ | |
86 | { | |
87 | omode = read_octal (mode); | |
88 | if (omode < 0) | |
89 | { | |
90 | builtin_error ("invalid file mode: %s", mode); | |
91 | return (EXECUTION_FAILURE); | |
92 | } | |
b72432fd JA |
93 | } |
94 | else if (mode) | |
95 | { | |
96 | /* initial bits are a=rwx; the mode argument modifies them */ | |
97 | omode = parse_symbolic_mode (mode, S_IRWXU | S_IRWXG | S_IRWXO); | |
98 | if (omode < 0) | |
99 | { | |
100 | builtin_error ("invalid file mode: %s", mode); | |
101 | return (EXECUTION_FAILURE); | |
102 | } | |
b72432fd JA |
103 | } |
104 | ||
105 | /* Make the new mode */ | |
106 | original_umask = umask (0); | |
107 | umask (original_umask); | |
108 | ||
109 | nmode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~original_umask; | |
ac50fbac | 110 | parent_mode = nmode | (S_IWUSR|S_IXUSR); /* u+wx */ |
b72432fd JA |
111 | |
112 | /* Adjust new mode based on mode argument */ | |
113 | nmode &= omode; | |
114 | ||
115 | for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next) | |
116 | { | |
117 | if (pflag && make_path (l->word->word, nmode, parent_mode)) | |
118 | { | |
119 | rval = EXECUTION_FAILURE; | |
120 | continue; | |
121 | } | |
122 | else if (pflag == 0 && mkdir (l->word->word, nmode) < 0) | |
123 | { | |
124 | builtin_error ("cannot create directory `%s': %s", l->word->word, strerror (errno)); | |
125 | rval = EXECUTION_FAILURE; | |
126 | } | |
127 | } | |
128 | return rval; | |
129 | } | |
130 | ||
131 | /* Make all the directories leading up to PATH, then create PATH. Note that | |
132 | this changes the process's umask; make sure that all paths leading to a | |
133 | return reset it to ORIGINAL_UMASK */ | |
134 | static int | |
135 | make_path (path, nmode, parent_mode) | |
136 | char *path; | |
137 | int nmode, parent_mode; | |
138 | { | |
139 | int oumask; | |
140 | struct stat sb; | |
141 | char *p, *npath; | |
142 | ||
143 | if (stat (path, &sb) == 0) | |
144 | { | |
145 | if (S_ISDIR (sb.st_mode) == 0) | |
146 | { | |
147 | builtin_error ("`%s': file exists but is not a directory", path); | |
148 | return 1; | |
149 | } | |
150 | ||
151 | if (chmod (path, nmode)) | |
152 | { | |
153 | builtin_error ("%s: %s", path, strerror (errno)); | |
154 | return 1; | |
155 | } | |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
160 | oumask = umask (0); | |
161 | npath = savestring (path); /* So we can write to it. */ | |
162 | ||
163 | /* Check whether or not we need to do anything with intermediate dirs. */ | |
164 | ||
165 | /* Skip leading slashes. */ | |
166 | p = npath; | |
167 | while (*p == '/') | |
168 | p++; | |
169 | ||
170 | while (p = strchr (p, '/')) | |
171 | { | |
172 | *p = '\0'; | |
173 | if (stat (npath, &sb) != 0) | |
174 | { | |
175 | if (mkdir (npath, parent_mode)) | |
176 | { | |
177 | builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); | |
178 | umask (original_umask); | |
179 | free (npath); | |
180 | return 1; | |
181 | } | |
182 | } | |
183 | else if (S_ISDIR (sb.st_mode) == 0) | |
184 | { | |
185 | builtin_error ("`%s': file exists but is not a directory", npath); | |
186 | umask (original_umask); | |
187 | free (npath); | |
188 | return 1; | |
189 | } | |
190 | ||
191 | *p++ = '/'; /* restore slash */ | |
192 | while (*p == '/') | |
193 | p++; | |
194 | } | |
195 | ||
196 | /* Create the final directory component. */ | |
197 | if (stat (npath, &sb) && mkdir (npath, nmode)) | |
198 | { | |
199 | builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); | |
200 | umask (original_umask); | |
201 | free (npath); | |
202 | return 1; | |
203 | } | |
204 | ||
205 | umask (original_umask); | |
206 | free (npath); | |
207 | return 0; | |
208 | } | |
209 | ||
210 | char *mkdir_doc[] = { | |
3185942a JA |
211 | "Create directories.", |
212 | "", | |
b72432fd JA |
213 | "Make directories. Create the directories named as arguments, in", |
214 | "the order specified, using mode rwxrwxrwx as modified by the current", | |
215 | "umask (see `help umask'). The -m option causes the file permission", | |
216 | "bits of the final directory to be MODE. The MODE argument may be", | |
217 | "an octal number or a symbolic mode like that used by chmod(1). If", | |
218 | "a symbolic mode is used, the operations are interpreted relative to", | |
219 | "an initial mode of \"a=rwx\". The -p option causes any required", | |
220 | "intermediate directories in PATH to be created. The directories", | |
221 | "are created with permssion bits of rwxrwxrwx as modified by the current", | |
222 | "umask, plus write and search permissions for the owner. mkdir", | |
223 | "returns 0 if the directories are created successfully, and non-zero", | |
224 | "if an error occurs.", | |
225 | (char *)NULL | |
226 | }; | |
227 | ||
228 | struct builtin mkdir_struct = { | |
229 | "mkdir", | |
230 | mkdir_builtin, | |
231 | BUILTIN_ENABLED, | |
232 | mkdir_doc, | |
233 | "mkdir [-p] [-m mode] directory [directory ...]", | |
234 | 0 | |
235 | }; |