1 /* $NetBSD: mkdir.c,v 1.31 2003/08/07 09:05:16 agc 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\n\ 35 The Regents of the University of California. All rights reserved.\n"); 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.31 2003/08/07 09:05:16 agc 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 #include <vis.h> 58 59 int stdout_ok; /* stdout connected to a terminal */ 60 61 int mkpath(char *, mode_t, mode_t); 62 void usage(void); 63 int main(int, char *[]); 64 char *printescaped(const char *); 65 66 int 67 main(int argc, char *argv[]) 68 { 69 int ch, exitval, pflag; 70 void *set; 71 mode_t mode, dir_mode; 72 73 setprogname(argv[0]); 74 (void)setlocale(LC_ALL, ""); 75 76 /* 77 * The default file mode is a=rwx (0777) with selected permissions 78 * removed in accordance with the file mode creation mask. For 79 * intermediate path name components, the mode is the default modified 80 * by u+wx so that the subdirectories can always be created. 81 */ 82 mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~umask(0); 83 dir_mode = mode | S_IWUSR | S_IXUSR; 84 85 pflag = 0; 86 while ((ch = getopt(argc, argv, "m:p")) != -1) 87 switch (ch) { 88 case 'p': 89 pflag = 1; 90 break; 91 case 'm': 92 if ((set = setmode(optarg)) == NULL) { 93 errx(EXIT_FAILURE, "invalid file mode: %s", 94 printescaped(optarg)); 95 /* NOTREACHED */ 96 } 97 mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); 98 free(set); 99 break; 100 case '?': 101 default: 102 usage(); 103 /* NOTREACHED */ 104 } 105 argc -= optind; 106 argv += optind; 107 108 if (*argv == NULL) { 109 usage(); 110 /* NOTREACHED */ 111 } 112 113 stdout_ok = isatty(STDOUT_FILENO); 114 115 for (exitval = EXIT_SUCCESS; *argv != NULL; ++argv) { 116 char *slash; 117 118 /* Remove trailing slashes, per POSIX. */ 119 slash = strrchr(*argv, '\0'); 120 while (--slash > *argv && *slash == '/') 121 *slash = '\0'; 122 123 if (pflag) { 124 if (mkpath(*argv, mode, dir_mode) < 0) 125 exitval = EXIT_FAILURE; 126 } else { 127 if (mkdir(*argv, mode) < 0) { 128 warn("%s", *argv); 129 exitval = EXIT_FAILURE; 130 } else { 131 /* 132 * The mkdir() and umask() calls both honor 133 * only the file permission bits, so if you try 134 * to set a mode including the sticky, setuid, 135 * setgid bits you lose them. So chmod(). 136 */ 137 if ((mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) != 0 && 138 chmod(*argv, mode) == -1) { 139 char *fn; 140 fn = printescaped(*argv); 141 warn("%s", fn); 142 free(fn); 143 exitval = EXIT_FAILURE; 144 } 145 } 146 } 147 } 148 exit(exitval); 149 /* NOTREACHED */ 150 } 151 152 /* 153 * mkpath -- create directories. 154 * path - path 155 * mode - file mode of terminal directory 156 * dir_mode - file mode of intermediate directories 157 */ 158 int 159 mkpath(char *path, mode_t mode, mode_t dir_mode) 160 { 161 struct stat sb; 162 char *slash, *fn; 163 int done, rv; 164 165 done = 0; 166 slash = path; 167 fn = printescaped(path); 168 169 for (;;) { 170 slash += strspn(slash, "/"); 171 slash += strcspn(slash, "/"); 172 173 done = (*slash == '\0'); 174 *slash = '\0'; 175 176 rv = mkdir(path, done ? mode : dir_mode); 177 if (rv < 0) { 178 /* 179 * Can't create; path exists or no perms. 180 * stat() path to determine what's there now. 181 */ 182 int sverrno; 183 184 sverrno = errno; 185 if (stat(path, &sb) < 0) { 186 /* Not there; use mkdir()s error */ 187 errno = sverrno; 188 warn("%s", fn); 189 free(fn); 190 return -1; 191 } 192 if (!S_ISDIR(sb.st_mode)) { 193 /* Is there, but isn't a directory */ 194 errno = ENOTDIR; 195 warn("%s", fn); 196 free(fn); 197 return -1; 198 } 199 } else if (done) { 200 /* 201 * Created ok, and this is the last element 202 */ 203 /* 204 * The mkdir() and umask() calls both honor only the 205 * file permission bits, so if you try to set a mode 206 * including the sticky, setuid, setgid bits you lose 207 * them. So chmod(). 208 */ 209 if ((mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) != 0 && 210 chmod(path, mode) == -1) { 211 warn("%s", fn); 212 free(fn); 213 return -1; 214 } 215 } 216 217 if (done) { 218 break; 219 } 220 *slash = '/'; 221 } 222 223 free(fn); 224 return 0; 225 } 226 227 void 228 usage(void) 229 { 230 231 (void)fprintf(stderr, "usage: %s [-p] [-m mode] dirname ...\n", 232 getprogname()); 233 exit(EXIT_FAILURE); 234 /* NOTREACHED */ 235 } 236 237 char * 238 printescaped(const char *src) 239 { 240 size_t len; 241 char *retval; 242 243 len = strlen(src); 244 if (len != 0 && SIZE_T_MAX/len <= 4) { 245 errx(EXIT_FAILURE, "%s: name too long", src); 246 /* NOTREACHED */ 247 } 248 249 retval = (char *)malloc(4*len+1); 250 if (retval != NULL) { 251 if (stdout_ok) 252 (void)strvis(retval, src, VIS_NL | VIS_CSTYLE); 253 else 254 (void)strcpy(retval, src); 255 return retval; 256 } else 257 errx(EXIT_FAILURE, "out of memory!"); 258 /* NOTREACHED */ 259 } 260