1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1983, 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #if 0 33 #ifndef lint 34 static char const copyright[] = 35 "@(#) Copyright (c) 1983, 1992, 1993\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #ifndef lint 40 static char sccsid[] = "@(#)mkdir.c 8.2 (Berkeley) 1/25/94"; 41 #endif /* not lint */ 42 #endif 43 #include <sys/cdefs.h> 44 __FBSDID("$FreeBSD$"); 45 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 49 #include <err.h> 50 #include <errno.h> 51 #include <libgen.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <sysexits.h> 56 #include <unistd.h> 57 58 static int build(char *, mode_t); 59 static void usage(void); 60 61 static int vflag; 62 63 int 64 main(int argc, char *argv[]) 65 { 66 int ch, exitval, success, pflag; 67 mode_t omode; 68 void *set = NULL; 69 char *mode; 70 71 omode = pflag = 0; 72 mode = NULL; 73 while ((ch = getopt(argc, argv, "m:pv")) != -1) 74 switch(ch) { 75 case 'm': 76 mode = optarg; 77 break; 78 case 'p': 79 pflag = 1; 80 break; 81 case 'v': 82 vflag = 1; 83 break; 84 case '?': 85 default: 86 usage(); 87 } 88 89 argc -= optind; 90 argv += optind; 91 if (argv[0] == NULL) 92 usage(); 93 94 if (mode == NULL) { 95 omode = S_IRWXU | S_IRWXG | S_IRWXO; 96 } else { 97 if ((set = setmode(mode)) == NULL) 98 errx(1, "invalid file mode: %s", mode); 99 omode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); 100 free(set); 101 } 102 103 for (exitval = 0; *argv != NULL; ++argv) { 104 if (pflag) { 105 success = build(*argv, omode); 106 } else if (mkdir(*argv, omode) < 0) { 107 if (errno == ENOTDIR || errno == ENOENT) 108 warn("%s", dirname(*argv)); 109 else 110 warn("%s", *argv); 111 success = 0; 112 } else { 113 success = 1; 114 if (vflag) 115 (void)printf("%s\n", *argv); 116 } 117 if (!success) 118 exitval = 1; 119 /* 120 * The mkdir() and umask() calls both honor only the low 121 * nine bits, so if you try to set a mode including the 122 * sticky, setuid, setgid bits you lose them. Don't do 123 * this unless the user has specifically requested a mode, 124 * as chmod will (obviously) ignore the umask. Do this 125 * on newly created directories only. 126 */ 127 if (success == 1 && mode != NULL && chmod(*argv, omode) == -1) { 128 warn("%s", *argv); 129 exitval = 1; 130 } 131 } 132 exit(exitval); 133 } 134 135 136 /* 137 * Returns 1 if a directory has been created, 138 * 2 if it already existed, and 0 on failure. 139 */ 140 static int 141 build(char *path, mode_t omode) 142 { 143 struct stat sb; 144 mode_t numask, oumask; 145 int first, last, retval; 146 char *p; 147 148 p = path; 149 oumask = 0; 150 retval = 1; 151 if (p[0] == '/') /* Skip leading '/'. */ 152 ++p; 153 for (first = 1, last = 0; !last ; ++p) { 154 if (p[0] == '\0') 155 last = 1; 156 else if (p[0] != '/') 157 continue; 158 *p = '\0'; 159 if (!last && p[1] == '\0') 160 last = 1; 161 if (first) { 162 /* 163 * POSIX 1003.2: 164 * For each dir operand that does not name an existing 165 * directory, effects equivalent to those caused by the 166 * following command shall occcur: 167 * 168 * mkdir -p -m $(umask -S),u+wx $(dirname dir) && 169 * mkdir [-m mode] dir 170 * 171 * We change the user's umask and then restore it, 172 * instead of doing chmod's. 173 */ 174 oumask = umask(0); 175 numask = oumask & ~(S_IWUSR | S_IXUSR); 176 (void)umask(numask); 177 first = 0; 178 } 179 if (last) 180 (void)umask(oumask); 181 if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) { 182 if (errno == EEXIST || errno == EISDIR) { 183 if (stat(path, &sb) < 0) { 184 warn("%s", path); 185 retval = 0; 186 break; 187 } else if (!S_ISDIR(sb.st_mode)) { 188 if (last) 189 errno = EEXIST; 190 else 191 errno = ENOTDIR; 192 warn("%s", path); 193 retval = 0; 194 break; 195 } 196 if (last) 197 retval = 2; 198 } else { 199 warn("%s", path); 200 retval = 0; 201 break; 202 } 203 } else if (vflag) 204 printf("%s\n", path); 205 if (!last) 206 *p = '/'; 207 } 208 if (!first && !last) 209 (void)umask(oumask); 210 return (retval); 211 } 212 213 static void 214 usage(void) 215 { 216 217 (void)fprintf(stderr, 218 "usage: mkdir [-pv] [-m mode] directory_name ...\n"); 219 exit (EX_USAGE); 220 } 221