1 /* $NetBSD: make_dirs.c,v 1.1.1.1 2009/06/23 10:09:00 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* make_dirs 3 6 /* SUMMARY 7 /* create directory hierarchy 8 /* SYNOPSIS 9 /* #include <make_dirs.h> 10 /* 11 /* int make_dirs(path, perms) 12 /* const char *path; 13 /* int perms; 14 /* DESCRIPTION 15 /* make_dirs() creates the directory specified in \fIpath\fR, and 16 /* creates any missing intermediate directories as well. Directories 17 /* are created with the permissions specified in \fIperms\fR, as 18 /* modified by the process umask. 19 /* DIAGNOSTICS: 20 /* Fatal: out of memory. make_dirs() returns 0 in case of success. 21 /* In case of problems. make_dirs() returns -1 and \fIerrno\fR 22 /* reflects the nature of the problem. 23 /* SEE ALSO 24 /* mkdir(2) 25 /* LICENSE 26 /* .ad 27 /* .fi 28 /* The Secure Mailer license must be distributed with this software. 29 /* AUTHOR(S) 30 /* Wietse Venema 31 /* IBM T.J. Watson Research 32 /* P.O. Box 704 33 /* Yorktown Heights, NY 10598, USA 34 /*--*/ 35 36 /* System library. */ 37 38 #include <sys_defs.h> 39 #include <sys/stat.h> 40 #include <errno.h> 41 #include <string.h> 42 43 /* Utility library. */ 44 45 #include "msg.h" 46 #include "mymalloc.h" 47 #include "stringops.h" 48 #include "make_dirs.h" 49 50 /* make_dirs - create directory hierarchy */ 51 52 int make_dirs(const char *path, int perms) 53 { 54 char *saved_path; 55 unsigned char *cp; 56 int saved_ch; 57 struct stat st; 58 int ret; 59 mode_t saved_mode = 0; 60 61 /* 62 * Initialize. Make a copy of the path that we can safely clobber. 63 */ 64 cp = (unsigned char *) (saved_path = mystrdup(path)); 65 66 /* 67 * I didn't like the 4.4BSD "mkdir -p" implementation, but coming up with 68 * my own took a day, spread out over several days. 69 */ 70 #define SKIP_WHILE(cond, ptr) { while(*ptr && (cond)) ptr++; } 71 72 SKIP_WHILE(*cp == '/', cp); 73 74 for (;;) { 75 SKIP_WHILE(*cp != '/', cp); 76 if ((saved_ch = *cp) != 0) 77 *cp = 0; 78 if ((ret = stat(saved_path, &st)) >= 0) { 79 if (!S_ISDIR(st.st_mode)) { 80 errno = ENOTDIR; 81 ret = -1; 82 break; 83 } 84 saved_mode = st.st_mode; 85 } else { 86 if (errno != ENOENT) 87 break; 88 89 /* 90 * mkdir(foo) fails with EEXIST if foo is a symlink. 91 */ 92 #if 0 93 94 /* 95 * Create a new directory. Unfortunately, mkdir(2) has no 96 * equivalent of open(2)'s O_CREAT|O_EXCL safety net, so we must 97 * require that the parent directory is not world writable. 98 * Detecting a lost race condition after the fact is not 99 * sufficient, as an attacker could repeat the attack and add one 100 * directory level at a time. 101 */ 102 if (saved_mode & S_IWOTH) { 103 msg_warn("refusing to mkdir %s: parent directory is writable by everyone", 104 saved_path); 105 errno = EPERM; 106 ret = -1; 107 break; 108 } 109 #endif 110 if ((ret = mkdir(saved_path, perms)) < 0) { 111 if (errno != EEXIST) 112 break; 113 /* Race condition? */ 114 if ((ret = stat(saved_path, &st)) < 0) 115 break; 116 if (!S_ISDIR(st.st_mode)) { 117 errno = ENOTDIR; 118 ret = -1; 119 break; 120 } 121 } 122 } 123 if (saved_ch != 0) 124 *cp = saved_ch; 125 SKIP_WHILE(*cp == '/', cp); 126 if (*cp == 0) 127 break; 128 } 129 130 /* 131 * Cleanup. 132 */ 133 myfree(saved_path); 134 return (ret); 135 } 136 137 #ifdef TEST 138 139 /* 140 * Test program. Usage: make_dirs path... 141 */ 142 #include <stdlib.h> 143 #include <msg_vstream.h> 144 145 int main(int argc, char **argv) 146 { 147 msg_vstream_init(argv[0], VSTREAM_ERR); 148 if (argc < 2) 149 msg_fatal("usage: %s path...", argv[0]); 150 while (--argc > 0 && *++argv != 0) 151 if (make_dirs(*argv, 0755)) 152 msg_fatal("%s: %m", *argv); 153 exit(0); 154 } 155 156 #endif 157