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