Merge branch 'vendor/LESS'
[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  */
33
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #include <err.h>
38 #include <errno.h>
39 #include <libgen.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sysexits.h>
44 #include <unistd.h>
45
46 static int      build(char *, mode_t);
47 static int      mkdir_race(const char *path, int nmode);
48 static void     usage(void);
49
50 static int      vflag;
51
52 int
53 main(int argc, char **argv)
54 {
55         int ch, exitval, success, pflag;
56         void *set;
57         mode_t omode;
58         char *mode;
59
60         pflag = 0;
61         mode = NULL;
62         while ((ch = getopt(argc, argv, "m:pv")) != -1)
63                 switch(ch) {
64                 case 'm':
65                         mode = optarg;
66                         break;
67                 case 'p':
68                         pflag = 1;
69                         break;
70                 case 'v':
71                         vflag = 1;
72                         break;
73                 default:
74                         usage();
75                 }
76
77         argc -= optind;
78         argv += optind;
79         if (argv[0] == NULL)
80                 usage();
81
82         if (mode == NULL) {
83                 omode = S_IRWXU | S_IRWXG | S_IRWXO;
84         } else {
85                 errno = 0;
86                 if ((set = setmode(mode)) == NULL) {
87                         if (!errno)
88                                 errx(1, "invalid file mode: %s", mode);
89                         else
90                                 /* A malloc(3) error, not a invaild file mode. */       
91                                 err(1, "setmode failed");
92                 }
93                 omode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
94                 free(set);
95         }
96
97         for (exitval = 0; *argv != NULL; ++argv) {
98                 success = 1;
99                 if (pflag) {
100                         if (build(*argv, omode))
101                                 success = 0;
102                 } else if (mkdir(*argv, omode) < 0) {
103                         if (errno == ENOTDIR || errno == ENOENT)
104                                 warn("%s", dirname(*argv));
105                         else
106                                 warn("%s", *argv);
107                         success = 0;
108                 } else if (vflag)
109                         printf("%s\n", *argv);
110                 
111                 if (!success)
112                         exitval = 1;
113                 /*
114                  * The mkdir() and umask() calls both honor only the low
115                  * nine bits, so if you try to set a mode including the
116                  * sticky, setuid, setgid bits you lose them.  Don't do
117                  * this unless the user has specifically requested a mode,
118                  * as chmod will (obviously) ignore the umask.
119                  */
120                 if (success && mode != NULL && chmod(*argv, omode) == -1) {
121                         warn("%s", *argv);
122                         exitval = 1;
123                 }
124         }
125         exit(exitval);
126 }
127
128 static int
129 build(char *path, mode_t omode)
130 {
131         struct stat sb;
132         mode_t numask, oumask;
133         int first, last, retval;
134         char *p;
135
136         p = path;
137         oumask = 0;
138         retval = 0;
139         if (p[0] == '/')                /* Skip leading '/'. */
140                 ++p;
141         for (first = 1, last = 0; !last ; ++p) {
142                 if (p[0] == '\0')
143                         last = 1;
144                 else if (p[0] != '/')
145                         continue;
146                 *p = '\0';
147                 if (!last && p[1] == '\0')
148                         last = 1;
149                 if (first) {
150                         /*
151                          * POSIX 1003.2:
152                          * For each dir operand that does not name an existing
153                          * directory, effects equivalent to those cased by the
154                          * following command shall occcur:
155                          *
156                          * mkdir -p -m $(umask -S),u+wx $(dirname dir) &&
157                          *    mkdir [-m mode] dir
158                          *
159                          * We change the user's umask and then restore it,
160                          * instead of doing chmod's.
161                          */
162                         oumask = umask(0);
163                         numask = oumask & ~(S_IWUSR | S_IXUSR);
164                         umask(numask);
165                         first = 0;
166                 }
167                 if (last)
168                         umask(oumask);
169                 if (stat(path, &sb)) {
170                         if (errno != ENOENT ||
171                             mkdir_race(path, last ? omode :
172                                   S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
173                                 warn("%s", path);
174                                 retval = 1;
175                                 break;
176                         } else if (vflag)
177                                 printf("%s\n", path);
178                 }
179                 else if ((sb.st_mode & S_IFMT) != S_IFDIR) {
180                         if (last)
181                                 errno = EEXIST;
182                         else
183                                 errno = ENOTDIR;
184                         warn("%s", path);
185                         retval = 1;
186                         break;
187                 }
188                 if (!last)
189                     *p = '/';
190         }
191         if (!first && !last)
192                 umask(oumask);
193         return (retval);
194 }
195
196 int
197 mkdir_race(const char *path, int nmode)
198 {
199         int res;
200         struct stat sb;
201
202         res = mkdir(path, nmode);
203         if (res < 0 && errno == EEXIST) {
204                if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode))
205                        return(0);
206                res = mkdir(path, nmode);
207         }
208         return (res);
209 }
210
211 static void
212 usage(void)
213 {
214
215         fprintf(stderr, "usage: mkdir [-pv] [-m mode] directory ...\n");
216         exit (EX_USAGE);
217 }