4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
32 #pragma ident "%Z%%M% %I% %E% SMI"
36 * If -m is used with a valid mode, directories will be
37 * created in that mode. Otherwise, the default mode will
38 * be 777 possibly altered by the process's file mode creation
40 * If -p is used, make the directory as well as
41 * its non-existing parent directories.
46 #include <sys/types.h>
57 #define MSGEXISTS "\"%s\": Exists but is not a directory\n"
58 #define MSGUSAGE "usage: mkdir [-m mode] [-p] dirname ...\n"
59 #define MSGFMT1 "\"%s\": %s\n"
60 #define MSGFAILED "Failed to make directory \"%s\"; %s\n"
62 extern int optind
, errno
;
66 *simplify(char *path
);
69 errmsg(int severity
, int code
, char *format
, ...);
72 newmode(char *ms
, mode_t new_mode
, mode_t umsk
, char *file
, char *path
);
74 #define ALLRWX (S_IRWXU | S_IRWXG | S_IRWXO)
78 main(int argc
, char *argv
[])
80 int pflag
, errflg
, mflag
;
81 int c
, local_errno
, tmp_errno
;
88 pflag
= mflag
= errflg
= 0;
91 (void) setlocale(LC_ALL
, "");
93 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
94 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
97 (void) textdomain(TEXT_DOMAIN
);
103 while ((c
= getopt(argc
, argv
, "m:p")) != EOF
) {
107 mode
= newmode(optarg
, ALLRWX
, cur_umask
, "", "");
120 * When using default ACLs, mkdir() should be called with
121 * 0777 always; and umask or default ACL should do the work.
122 * Because of the POSIX.2 requirement that the
123 * intermediate mode be at least -wx------,
124 * we do some trickery here.
126 * If pflag is not set, we can just leave the umask as
127 * it the user specified it, unless it masks any of bits 0300.
130 modediff
= cur_umask
& (S_IXUSR
| S_IWUSR
);
132 cur_umask
&= ~modediff
;
134 (void) umask(cur_umask
);
137 if (argc
< 1 || errflg
) {
138 errmsg(0, 2, gettext(MSGUSAGE
));
140 argv
= &argv
[optind
];
144 if ((d
= simplify(*argv
++)) == NULL
) {
149 * When -p is set, invokes mkdirp library routine.
150 * Although successfully invoked, mkdirp sets errno to ENOENT
151 * if one of the directory in the pathname does not exist,
152 * thus creates a confusion on success/failure status
153 * possibly checked by the calling routine or shell.
154 * Therefore, errno is reset only when
155 * mkdirp has executed successfully, otherwise save
160 * POSIX.2 says that it is not an error if
161 * the argument names an existing directory.
162 * We will, however, complain if the argument
163 * exists but is not a directory.
165 if (lstat(d
, &buf
) != -1) {
166 if (S_ISDIR(buf
.st_mode
)) {
169 local_errno
= EEXIST
;
170 errmsg(0, 0, gettext(MSGEXISTS
), d
);
176 if (mkdirp(d
, ALLRWX
) < 0) {
179 if (tmp_errno
== EEXIST
) {
180 if (lstat(d
, &buf
) != -1) {
181 if (! S_ISDIR(buf
.st_mode
)) {
184 errmsg(0, 0, gettext(
188 /* S_ISDIR: do nothing */
190 local_errno
= tmp_errno
;
193 gettext(MSGFAILED
), d
,
194 strerror(local_errno
));
198 local_errno
= tmp_errno
;
199 errmsg(0, 0, gettext(MSGFMT1
), d
,
200 strerror(tmp_errno
));
208 * get the file mode for the newly
209 * created directory and test for
210 * set gid bit being inherited from the parent
211 * directory to include it with the file
212 * mode creation for the last directory
215 * This is only needed if mflag was specified
216 * or if the umask was adjusted with -wx-----
218 * If mflag is specified, we chmod to the specified
219 * mode, oring in the 02000 bit.
221 * If modediff is set, those bits need to be
222 * removed from the last directory component,
223 * all other bits are kept regardless of umask
224 * in case a default ACL is present.
226 if (mflag
|| modediff
) {
229 (void) lstat(d
, &buf
);
230 if (modediff
&& !mflag
)
231 tmpmode
= (buf
.st_mode
& 07777)
234 tmpmode
= mode
| (buf
.st_mode
& 02000);
236 if (chmod(d
, tmpmode
) < 0) {
239 errmsg(0, 0, gettext(MSGFMT1
), d
,
240 strerror(tmp_errno
));
249 * No -p. Make only one directory
254 if (mkdir(d
, mode
) < 0) {
255 local_errno
= tmp_errno
= errno
;
256 errmsg(0, 0, gettext(MSGFAILED
), d
,
257 strerror(tmp_errno
));
262 (void) lstat(d
, &buf
);
263 tmpmode
= mode
| (buf
.st_mode
& 02000);
265 if (chmod(d
, tmpmode
) < 0) {
268 errmsg(0, 0, gettext(MSGFMT1
), d
,
269 strerror(tmp_errno
));
277 /* When pflag is set, the errno is saved in local_errno */
281 return (errno
? 2: 0);
285 * errmsg - This is an interface required by the code common to mkdir and
286 * chmod. The severity parameter is ignored here, but is meaningful
293 errmsg(int severity
, int code
, char *format
, ...)
296 va_start(ap
, format
);
298 (void) fprintf(stderr
, "mkdir: ");
299 (void) vfprintf(stderr
, format
, ap
);
309 * simplify - given a pathname in a writable buffer, simplify that
310 * path by removing meaningless occurances of path
313 * The change happens in place in the argument. The
314 * result is neceassarily no longer than the original.
316 * Return the pointer supplied by the caller on success, or
319 * The caller should handle error reporting based upon the
324 simplify(char *mbPath
)
327 size_t mbPathlen
; /* length of multi-byte path */
328 size_t wcPathlen
; /* length of wide-character path */
329 wchar_t *wptr
; /* scratch pointer */
330 wchar_t *wcPath
; /* wide-character version of the path */
333 * bail out if there is nothing there.
340 * convert the multi-byte version of the path to a
341 * wide-character rendering, for doing our figuring.
344 mbPathlen
= strlen(mbPath
);
346 if ((wcPath
= calloc(sizeof (wchar_t), mbPathlen
+1)) == NULL
) {
351 if ((wcPathlen
= mbstowcs(wcPath
, mbPath
, mbPathlen
)) == (size_t)-1) {
357 * remove duplicate slashes first ("//../" -> "/")
360 for (wptr
= wcPath
, i
= 0; i
< wcPathlen
; i
++) {
363 if (wcPath
[i
] == '/') {
366 while (wcPath
[i
] == '/') {
377 * next skip initial occurances of "./"
380 for (wcPathlen
= wcslen(wcPath
), wptr
= wcPath
, i
= 0;
381 i
< wcPathlen
-2 && wcPath
[i
] == '.' && wcPath
[i
+1] == '/';
387 * now make reductions of various forms.
390 while (i
< wcPathlen
) {
391 if (i
< wcPathlen
-2 && wcPath
[i
] == '/' &&
392 wcPath
[i
+1] == '.' && wcPath
[i
+2] == '/') {
396 /* Normal case: copy the character */
397 *wptr
++ = wcPath
[i
++];
404 * now convert back to the multi-byte format.
407 if (wcstombs(mbPath
, wcPath
, mbPathlen
) == (size_t)-1) {