Correct BSD License clause numbering from 1-2-4 to 1-2-3.
[dragonfly.git] / bin / mkdir / mkdir.c
1 /*
2  * Copyright (c) 1983, 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1983, 1992, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)mkdir.c  8.2 (Berkeley) 1/25/94
31  * $FreeBSD: src/bin/mkdir/mkdir.c,v 1.19.2.2 2001/08/01 04:42:37 obrien Exp $
32  * $DragonFly: src/bin/mkdir/mkdir.c,v 1.6 2004/11/07 20:54:51 eirikn Exp $
33  */
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37
38 #include <err.h>
39 #include <errno.h>
40 #include <libgen.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sysexits.h>
45 #include <unistd.h>
46
47 static int      build(char *, mode_t);
48 static int      mkdir_race(const char *path, int nmode);
49 static void     usage(void);
50
51 int vflag;
52
53 int
54 main(int argc, char **argv)
55 {
56         int ch, exitval, success, pflag;
57         void *set;
58         mode_t omode;
59         char *mode;
60
61         pflag = 0;
62         mode = NULL;
63         while ((ch = getopt(argc, argv, "m:pv")) != -1)
64                 switch(ch) {
65                 case 'm':
66                         mode = optarg;
67                         break;
68                 case 'p':
69                         pflag = 1;
70                         break;
71                 case 'v':
72                         vflag = 1;
73                         break;
74                 default:
75                         usage();
76                 }
77
78         argc -= optind;
79         argv += optind;
80         if (argv[0] == NULL)
81                 usage();
82
83         if (mode == NULL) {
84                 omode = S_IRWXU | S_IRWXG | S_IRWXO;
85         } else {
86                 errno = 0;
87                 if ((set = setmode(mode)) == NULL) {
88                         if (!errno)
89                                 errx(1, "invalid file mode: %s", mode);
90                         else
91                                 /* A malloc(3) error, not a invaild file mode. */       
92                                 err(1, "setmode failed");
93                 }
94                 omode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
95                 free(set);
96         }
97
98         for (exitval = 0; *argv != NULL; ++argv) {
99                 success = 1;
100                 if (pflag) {
101                         if (build(*argv, omode))
102                                 success = 0;
103                 } else if (mkdir(*argv, omode) < 0) {
104                         if (errno == ENOTDIR || errno == ENOENT)
105                                 warn("%s", dirname(*argv));
106                         else
107                                 warn("%s", *argv);
108                         success = 0;
109                 } else if (vflag)
110                         printf("%s\n", *argv);
111                 
112                 if (!success)
113                         exitval = 1;
114                 /*
115                  * The mkdir() and umask() calls both honor only the low
116                  * nine bits, so if you try to set a mode including the
117                  * sticky, setuid, setgid bits you lose them.  Don't do
118                  * this unless the user has specifically requested a mode,
119                  * as chmod will (obviously) ignore the umask.
120                  */
121                 if (success && mode != NULL && chmod(*argv, omode) == -1) {
122                         warn("%s", *argv);
123                         exitval = 1;
124                 }
125         }
126         exit(exitval);
127 }
128
129 static int
130 build(char *path, mode_t omode)
131 {
132         struct stat sb;
133         mode_t numask, oumask;
134         int first, last, retval;
135         char *p;
136
137         p = path;
138         oumask = 0;
139         retval = 0;
140         if (p[0] == '/')                /* Skip leading '/'. */
141                 ++p;
142         for (first = 1, last = 0; !last ; ++p) {
143                 if (p[0] == '\0')
144                         last = 1;
145                 else if (p[0] != '/')
146                         continue;
147                 *p = '\0';
148                 if (!last && p[1] == '\0')
149                         last = 1;
150                 if (first) {
151                         /*
152                          * POSIX 1003.2:
153                          * For each dir operand that does not name an existing
154                          * directory, effects equivalent to those cased by the
155                          * following command shall occcur:
156                          *
157                          * mkdir -p -m $(umask -S),u+wx $(dirname dir) &&
158                          *    mkdir [-m mode] dir
159                          *
160                          * We change the user's umask and then restore it,
161                          * instead of doing chmod's.
162                          */
163                         oumask = umask(0);
164                         numask = oumask & ~(S_IWUSR | S_IXUSR);
165                         umask(numask);
166                         first = 0;
167                 }
168                 if (last)
169                         umask(oumask);
170                 if (stat(path, &sb)) {
171                         if (errno != ENOENT ||
172                             mkdir_race(path, last ? omode :
173                                   S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
174                                 warn("%s", path);
175                                 retval = 1;
176                                 break;
177                         } else if (vflag)
178                                 printf("%s\n", path);
179                 }
180                 else if ((sb.st_mode & S_IFMT) != S_IFDIR) {
181                         if (last)
182                                 errno = EEXIST;
183                         else
184                                 errno = ENOTDIR;
185                         warn("%s", path);
186                         retval = 1;
187                         break;
188                 }
189                 if (!last)
190                     *p = '/';
191         }
192         if (!first && !last)
193                 umask(oumask);
194         return (retval);
195 }
196
197 int
198 mkdir_race(const char *path, int nmode)
199 {
200         int res;
201         struct stat sb;
202
203         res = mkdir(path, nmode);
204         if (res < 0 && errno == EEXIST) {
205                if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode))
206                        return(0);
207                res = mkdir(path, nmode);
208         }
209         return (res);
210 }
211
212 static void
213 usage(void)
214 {
215
216         fprintf(stderr, "usage: mkdir [-pv] [-m mode] directory ...\n");
217         exit (EX_USAGE);
218 }