1 /* $NetBSD: mkdir.c,v 1.37 2008/07/20 00:52:40 lukem Exp $ */ 2 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 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1983, 1992, 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)mkdir.c 8.2 (Berkeley) 1/25/94"; 41 #else 42 __RCSID("$NetBSD: mkdir.c,v 1.37 2008/07/20 00:52:40 lukem Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/stat.h> 48 #include <sys/types.h> 49 50 #include <err.h> 51 #include <errno.h> 52 #include <locale.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <unistd.h> 57 58 int mkpath(char *, mode_t, mode_t); 59 void usage(void); 60 int main(int, char *[]); 61 62 int 63 main(int argc, char *argv[]) 64 { 65 int ch, exitval, pflag; 66 void *set; 67 mode_t mode, dir_mode; 68 69 setprogname(argv[0]); 70 (void)setlocale(LC_ALL, ""); 71 72 /* 73 * The default file mode is a=rwx (0777) with selected permissions 74 * removed in accordance with the file mode creation mask. For 75 * intermediate path name components, the mode is the default modified 76 * by u+wx so that the subdirectories can always be created. 77 */ 78 mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~umask(0); 79 dir_mode = mode | S_IWUSR | S_IXUSR; 80 81 pflag = 0; 82 while ((ch = getopt(argc, argv, "m:p")) != -1) 83 switch (ch) { 84 case 'p': 85 pflag = 1; 86 break; 87 case 'm': 88 if ((set = setmode(optarg)) == NULL) { 89 err(EXIT_FAILURE, "Cannot set file mode `%s'", 90 optarg); 91 /* NOTREACHED */ 92 } 93 mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); 94 free(set); 95 break; 96 case '?': 97 default: 98 usage(); 99 /* NOTREACHED */ 100 } 101 argc -= optind; 102 argv += optind; 103 104 if (*argv == NULL) { 105 usage(); 106 /* NOTREACHED */ 107 } 108 109 for (exitval = EXIT_SUCCESS; *argv != NULL; ++argv) { 110 #ifdef notdef 111 char *slash; 112 113 /* Kernel takes care of this */ 114 /* Remove trailing slashes, per POSIX. */ 115 slash = strrchr(*argv, '\0'); 116 while (--slash > *argv && *slash == '/') 117 *slash = '\0'; 118 #endif 119 120 if (pflag) { 121 if (mkpath(*argv, mode, dir_mode) < 0) 122 exitval = EXIT_FAILURE; 123 } else { 124 if (mkdir(*argv, mode) < 0) { 125 warn("%s", *argv); 126 exitval = EXIT_FAILURE; 127 } else { 128 /* 129 * The mkdir() and umask() calls both honor 130 * only the file permission bits, so if you try 131 * to set a mode including the sticky, setuid, 132 * setgid bits you lose them. So chmod(). 133 */ 134 if ((mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) != 0 && 135 chmod(*argv, mode) == -1) { 136 warn("%s", *argv); 137 exitval = EXIT_FAILURE; 138 } 139 } 140 } 141 } 142 exit(exitval); 143 /* NOTREACHED */ 144 } 145 146 /* 147 * mkpath -- create directories. 148 * path - path 149 * mode - file mode of terminal directory 150 * dir_mode - file mode of intermediate directories 151 */ 152 int 153 mkpath(char *path, mode_t mode, mode_t dir_mode) 154 { 155 struct stat sb; 156 char *slash; 157 int done, rv; 158 159 done = 0; 160 slash = path; 161 162 for (;;) { 163 slash += strspn(slash, "/"); 164 slash += strcspn(slash, "/"); 165 166 done = (*slash == '\0'); 167 *slash = '\0'; 168 169 rv = mkdir(path, done ? mode : dir_mode); 170 if (rv < 0) { 171 /* 172 * Can't create; path exists or no perms. 173 * stat() path to determine what's there now. 174 */ 175 int sverrno; 176 177 sverrno = errno; 178 if (stat(path, &sb) < 0) { 179 /* Not there; use mkdir()s error */ 180 errno = sverrno; 181 warn("%s", path); 182 return -1; 183 } 184 if (!S_ISDIR(sb.st_mode)) { 185 /* Is there, but isn't a directory */ 186 errno = ENOTDIR; 187 warn("%s", path); 188 return -1; 189 } 190 } else if (done) { 191 /* 192 * Created ok, and this is the last element 193 */ 194 /* 195 * The mkdir() and umask() calls both honor only the 196 * file permission bits, so if you try to set a mode 197 * including the sticky, setuid, setgid bits you lose 198 * them. So chmod(). 199 */ 200 if ((mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) != 0 && 201 chmod(path, mode) == -1) { 202 warn("%s", path); 203 return -1; 204 } 205 } 206 207 if (done) { 208 break; 209 } 210 *slash = '/'; 211 } 212 213 return 0; 214 } 215 216 void 217 usage(void) 218 { 219 220 (void)fprintf(stderr, "usage: %s [-p] [-m mode] dirname ...\n", 221 getprogname()); 222 exit(EXIT_FAILURE); 223 /* NOTREACHED */ 224 } 225