1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1992-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                                                                      *
20 ***********************************************************************/
21 #pragma prototyped
22 /*
23  * David Korn
24  * AT&T Bell Laboratories
25  *
26  * mkdir
27  */
28 
29 static const char usage[] =
30 "[-?\n@(#)$Id: mkdir (AT&T Research) 2010-04-08 $\n]"
31 USAGE_LICENSE
32 "[+NAME?mkdir - make directories]"
33 "[+DESCRIPTION?\bmkdir\b creates one or more directories.  By "
34 	"default, the mode of created directories is \ba=rwx\b minus the "
35 	"bits set in the \bumask\b(1).]"
36 "[m:mode]:[mode?Set the mode of created directories to \amode\a.  "
37 	"\amode\a is symbolic or octal mode as in \bchmod\b(1).  Relative "
38 	"modes assume an initial mode of \ba=rwx\b.]"
39 "[p:parents?Create any missing intermediate pathname components. For "
40     "each dir operand that does not name an existing directory, effects "
41     "equivalent to those caused by the following command shall occur: "
42     "\vmkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode]] "
43     "dir\v where the \b-m\b mode option represents that option supplied to "
44     "the original invocation of \bmkdir\b, if any. Each dir operand that "
45     "names an existing directory shall be ignored without error.]"
46 "[v:verbose?Print a message on the standard error for each created "
47     "directory.]"
48 "\n"
49 "\ndirectory ...\n"
50 "\n"
51 "[+EXIT STATUS?]{"
52         "[+0?All directories created successfully, or the \b-p\b option "
53 	"was specified and all the specified directories now exist.]"
54         "[+>0?An error occurred.]"
55 "}"
56 "[+SEE ALSO?\bchmod\b(1), \brmdir\b(1), \bumask\b(1)]"
57 ;
58 
59 #include <cmd.h>
60 #include <ls.h>
61 
62 #define DIRMODE	(S_IRWXU|S_IRWXG|S_IRWXO)
63 
64 int
b_mkdir(int argc,char ** argv,Shbltin_t * context)65 b_mkdir(int argc, char** argv, Shbltin_t* context)
66 {
67 	register char*	path;
68 	register int	n;
69 	register mode_t	mode = DIRMODE;
70 	register mode_t	mask = 0;
71 	register int	mflag = 0;
72 	register int	pflag = 0;
73 	register int	vflag = 0;
74 	int		made;
75 	char*		part;
76 	mode_t		dmode;
77 	struct stat	st;
78 
79 	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
80 	for (;;)
81 	{
82 		switch (optget(argv, usage))
83 		{
84 		case 'm':
85 			mflag = 1;
86 			mode = strperm(opt_info.arg, &part, mode);
87 			if (*part)
88 				error(ERROR_exit(0), "%s: invalid mode", opt_info.arg);
89 			continue;
90 		case 'p':
91 			pflag = 1;
92 			continue;
93 		case 'v':
94 			vflag = 1;
95 			continue;
96 		case ':':
97 			error(2, "%s", opt_info.arg);
98 			break;
99 		case '?':
100 			error(ERROR_usage(2), "%s", opt_info.arg);
101 			break;
102 		}
103 		break;
104 	}
105 	argv += opt_info.index;
106 	if (error_info.errors || !*argv)
107 		error(ERROR_usage(2), "%s", optusage(NiL));
108 	mask = umask(0);
109 	if (mflag || pflag)
110 	{
111 		dmode = DIRMODE & ~mask;
112 		if (!mflag)
113 			mode = dmode;
114 		dmode |= S_IWUSR | S_IXUSR;
115 	}
116 	else
117 	{
118 		mode &= ~mask;
119 		umask(mask);
120 		mask = 0;
121 	}
122 	while (path = *argv++)
123 	{
124 		if (!mkdir(path, mode))
125 		{
126 			if (vflag)
127 				error(0, "%s: directory created", path);
128 			made = 1;
129 		}
130 		else if (!pflag || !(errno == ENOENT || errno == EEXIST || errno == ENOTDIR))
131 		{
132 			error(ERROR_system(0), "%s:", path);
133 			continue;
134 		}
135 		else if (errno == EEXIST)
136 			continue;
137 		else
138 		{
139 			/*
140 			 * -p option, preserve intermediates
141 			 * first eliminate trailing /'s
142 			 */
143 
144 			made = 0;
145 			n = strlen(path);
146 			while (n > 0 && path[--n] == '/');
147 			path[n + 1] = 0;
148 			for (part = path, n = *part; n;)
149 			{
150 				/* skip over slashes */
151 				while (*part == '/')
152 					part++;
153 				/* skip to next component */
154 				while ((n = *part) && n != '/')
155 					part++;
156 				*part = 0;
157 				if (mkdir(path, n ? dmode : mode) < 0 && errno != EEXIST && access(path, F_OK) < 0)
158 				{
159 					error(ERROR_system(0), "%s: cannot create intermediate directory", path);
160 					*part = n;
161 					break;
162 				}
163 				if (vflag)
164 					error(0, "%s: directory created", path);
165 				if (!(*part = n))
166 				{
167 					made = 1;
168 					break;
169 				}
170 			}
171 		}
172 		if (made && (mode & (S_ISVTX|S_ISUID|S_ISGID)))
173 		{
174 			if (stat(path, &st))
175 			{
176 				error(ERROR_system(0), "%s: cannot stat", path);
177 				break;
178 			}
179 			if ((st.st_mode & (S_ISVTX|S_ISUID|S_ISGID)) != (mode & (S_ISVTX|S_ISUID|S_ISGID)) && chmod(path, mode))
180 			{
181 				error(ERROR_system(0), "%s: cannot change mode from %s to %s", path, fmtperm(st.st_mode & (S_ISVTX|S_ISUID|S_ISGID)), fmtperm(mode));
182 				break;
183 			}
184 		}
185 	}
186 	if (mask)
187 		umask(mask);
188 	return error_info.errors != 0;
189 }
190