1 /* vi: set sw=4 ts=4: */
2 /*
3  * parse_mode implementation for busybox
4  *
5  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8  */
9 /* Mar 5, 2003    Manuel Novoa III
10  *
11  * This is the main work function for the 'mkdir' applet.  As such, it
12  * strives to be SUSv3 compliant in it's behaviour when recursively
13  * making missing parent dirs, and in it's mode setting of the final
14  * directory 'path'.
15  *
16  * To recursively build all missing intermediate directories, make
17  * sure that (flags & FILEUTILS_RECUR) is non-zero.  Newly created
18  * intermediate directories will have at least u+wx perms.
19  *
20  * To set specific permissions on 'path', pass the appropriate 'mode'
21  * val.  Otherwise, pass -1 to get default permissions.
22  */
23 #include "libbb.h"
24 
25 /* This function is used from NOFORK applets. It must not allocate anything */
26 
bb_make_directory(char * path,long mode,int flags)27 int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
28 {
29 	mode_t cur_mask;
30 	mode_t org_mask;
31 	const char *fail_msg;
32 	char *s;
33 	char c;
34 	struct stat st;
35 
36 	/* "path" can be a result of dirname().
37 	 * dirname("no_slashes") returns ".", possibly read-only.
38 	 * musl dirname() can return read-only "/" too.
39 	 * We need writable string. And for "/", "." (and ".."?)
40 	 * nothing needs to be created anyway.
41 	 */
42 	if (LONE_CHAR(path, '/'))
43 		return 0;
44 	if (path[0] == '.') {
45 		if (path[1] == '\0')
46 			return 0; /* "." */
47 //		if (path[1] == '.' && path[2] == '\0')
48 //			return 0; /* ".." */
49 	}
50 
51 	org_mask = cur_mask = (mode_t)-1L;
52 	s = path;
53 	while (1) {
54 		c = '\0';
55 
56 		if (flags & FILEUTILS_RECUR) {  /* Get the parent */
57 			/* Bypass leading non-'/'s and then subsequent '/'s */
58 			while (*s) {
59 				if (*s == '/') {
60 					do {
61 						++s;
62 					} while (*s == '/');
63 					c = *s; /* Save the current char */
64 					*s = '\0'; /* and replace it with nul */
65 					break;
66 				}
67 				++s;
68 			}
69 		}
70 
71 		if (c != '\0') {
72 			/* Intermediate dirs: must have wx for user */
73 			if (cur_mask == (mode_t)-1L) { /* wasn't done yet? */
74 				mode_t new_mask;
75 				org_mask = umask(0);
76 				cur_mask = 0;
77 				/* Clear u=wx in umask - this ensures
78 				 * they won't be cleared on mkdir */
79 				new_mask = (org_mask & ~(mode_t)0300);
80 				//bb_error_msg("org_mask:%o cur_mask:%o", org_mask, new_mask);
81 				if (new_mask != cur_mask) {
82 					cur_mask = new_mask;
83 					umask(new_mask);
84 				}
85 			}
86 		} else {
87 			/* Last component: uses original umask */
88 			//bb_error_msg("1 org_mask:%o", org_mask);
89 			if (org_mask != cur_mask) {
90 				cur_mask = org_mask;
91 				umask(org_mask);
92 			}
93 		}
94 
95 		//bb_error_msg("mkdir '%s'", path);
96 		if (mkdir(path, 0777) < 0) {
97 			/* If we failed for any other reason than the directory
98 			 * already exists, output a diagnostic and return -1 */
99 			if ((errno != EEXIST && errno != EISDIR)
100 			 || !(flags & FILEUTILS_RECUR)
101 			 || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
102 			) {
103 				fail_msg = "create";
104 				break;
105 			}
106 			/* Since the directory exists, don't attempt to change
107 			 * permissions if it was the full target.  Note that
108 			 * this is not an error condition. */
109 			if (!c) {
110 				goto ret0;
111 			}
112 		} else {
113 			if (flags & FILEUTILS_VERBOSE) {
114 				printf("created directory: '%s'\n", path);
115 			}
116 		}
117 
118 		if (!c) {
119 			/* Done.  If necessary, update perms on the newly
120 			 * created directory.  Failure to update here _is_
121 			 * an error. */
122 			if (mode != -1) {
123 				//bb_error_msg("chmod 0%03lo mkdir '%s'", mode, path);
124 				if (chmod(path, mode) < 0) {
125 					fail_msg = "set permissions of";
126 					if (flags & FILEUTILS_IGNORE_CHMOD_ERR) {
127 						flags = 0;
128 						goto print_err;
129 					}
130 					break;
131 				}
132 			}
133 			goto ret0;
134 		}
135 
136 		/* Remove any inserted nul from the path (recursive mode) */
137 		*s = c;
138 	} /* while (1) */
139 
140 	flags = -1;
141  print_err:
142 	bb_perror_msg("can't %s directory '%s'", fail_msg, path);
143 	goto ret;
144  ret0:
145 	flags = 0;
146  ret:
147 	//bb_error_msg("2 org_mask:%o", org_mask);
148 	if (org_mask != cur_mask)
149 		umask(org_mask);
150 	return flags;
151 }
152