Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / make_dirs.c
blob384925ab67bce3b785a27c0c196bf61cc068d806
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* make_dirs 3
6 /* SUMMARY
7 /* create directory hierarchy
8 /* SYNOPSIS
9 /* #include <make_dirs.h>
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 /*--*/
36 /* System library. */
38 #include <sys_defs.h>
39 #include <sys/stat.h>
40 #include <errno.h>
41 #include <string.h>
43 /* Utility library. */
45 #include "msg.h"
46 #include "mymalloc.h"
47 #include "stringops.h"
48 #include "make_dirs.h"
50 /* make_dirs - create directory hierarchy */
52 int make_dirs(const char *path, int perms)
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;
62 * Initialize. Make a copy of the path that we can safely clobber.
64 cp = (unsigned char *) (saved_path = mystrdup(path));
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.
70 #define SKIP_WHILE(cond, ptr) { while(*ptr && (cond)) ptr++; }
72 SKIP_WHILE(*cp == '/', cp);
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;
84 saved_mode = st.st_mode;
85 } else {
86 if (errno != ENOENT)
87 break;
90 * mkdir(foo) fails with EEXIST if foo is a symlink.
92 #if 0
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.
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;
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;
123 if (saved_ch != 0)
124 *cp = saved_ch;
125 SKIP_WHILE(*cp == '/', cp);
126 if (*cp == 0)
127 break;
131 * Cleanup.
133 myfree(saved_path);
134 return (ret);
137 #ifdef TEST
140 * Test program. Usage: make_dirs path...
142 #include <stdlib.h>
143 #include <msg_vstream.h>
145 int main(int argc, char **argv)
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);
156 #endif