blob: 9b03bb8d0f461945c37fd378725ca0e4ed39612c [file] [log] [blame]
Matt Kraaiceeff732001-06-21 19:41:37 +00001/* vi: set sw=4 ts=4: */
2/*
Manuel Novoa III cad53642003-03-19 09:13:01 +00003 * parse_mode implementation for busybox
Matt Kraaiceeff732001-06-21 19:41:37 +00004 *
Manuel Novoa III cad53642003-03-19 09:13:01 +00005 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
Matt Kraaiceeff732001-06-21 19:41:37 +00006 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02007 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Matt Kraaiceeff732001-06-21 19:41:37 +00008 */
Manuel Novoa III cad53642003-03-19 09:13:01 +00009/* 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 *
Eric Andersenaff114c2004-04-14 17:51:38 +000020 * To set specific permissions on 'path', pass the appropriate 'mode'
21 * val. Otherwise, pass -1 to get default permissions.
Manuel Novoa III cad53642003-03-19 09:13:01 +000022 */
Matt Kraaiceeff732001-06-21 19:41:37 +000023#include "libbb.h"
24
Denis Vlasenko99912ca2007-04-10 15:43:37 +000025/* This function is used from NOFORK applets. It must not allocate anything */
26
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +000027int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
Matt Kraaiceeff732001-06-21 19:41:37 +000028{
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +010029 mode_t cur_mask;
30 mode_t org_mask;
Manuel Novoa III cad53642003-03-19 09:13:01 +000031 const char *fail_msg;
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +010032 char *s;
Manuel Novoa III cad53642003-03-19 09:13:01 +000033 char c;
Eric Andersen28426592004-10-08 07:21:58 +000034 struct stat st;
Glenn L McGrath822e7fd2002-11-24 22:48:20 +000035
Denys Vlasenko4bd0c2a2016-12-04 10:42:07 +010036 /* "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, '/'))
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +010043 return 0;
Denys Vlasenko4bd0c2a2016-12-04 10:42:07 +010044 if (path[0] == '.') {
45 if (path[1] == '\0')
46 return 0; /* "." */
47// if (path[1] == '.' && path[2] == '\0')
48// return 0; /* ".." */
49 }
Glenn L McGrath822e7fd2002-11-24 22:48:20 +000050
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +010051 org_mask = cur_mask = (mode_t)-1L;
52 s = path;
Denis Vlasenko11152e32008-08-15 19:18:35 +000053 while (1) {
54 c = '\0';
Glenn L McGrath5b110872002-11-24 23:22:29 +000055
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020056 if (flags & FILEUTILS_RECUR) { /* Get the parent */
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +010057 /* Bypass leading non-'/'s and then subsequent '/'s */
Manuel Novoa III cad53642003-03-19 09:13:01 +000058 while (*s) {
59 if (*s == '/') {
60 do {
61 ++s;
62 } while (*s == '/');
Denis Vlasenko11152e32008-08-15 19:18:35 +000063 c = *s; /* Save the current char */
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +010064 *s = '\0'; /* and replace it with nul */
Manuel Novoa III cad53642003-03-19 09:13:01 +000065 break;
66 }
67 ++s;
68 }
Glenn L McGrath5b110872002-11-24 23:22:29 +000069 }
70
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +010071 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 }
Denis Vlasenko11152e32008-08-15 19:18:35 +000094
Denys Vlasenko5cdd1202018-02-06 17:59:32 +010095 //bb_error_msg("mkdir '%s'", path);
Manuel Novoa III cad53642003-03-19 09:13:01 +000096 if (mkdir(path, 0777) < 0) {
97 /* If we failed for any other reason than the directory
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +010098 * already exists, output a diagnostic and return -1 */
Jeremie Koenig84b01d52010-05-27 15:46:33 +020099 if ((errno != EEXIST && errno != EISDIR)
Denis Vlasenko11152e32008-08-15 19:18:35 +0000100 || !(flags & FILEUTILS_RECUR)
101 || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
102 ) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000103 fail_msg = "create";
Manuel Novoa III cad53642003-03-19 09:13:01 +0000104 break;
105 }
106 /* Since the directory exists, don't attempt to change
107 * permissions if it was the full target. Note that
Denis Vlasenko582dff02008-10-19 19:36:30 +0000108 * this is not an error condition. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000109 if (!c) {
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +0100110 goto ret0;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000111 }
Denys Vlasenko17f84182014-05-19 16:23:50 +0200112 } else {
113 if (flags & FILEUTILS_VERBOSE) {
114 printf("created directory: '%s'\n", path);
115 }
Glenn L McGrath193697d2002-08-24 20:11:38 +0000116 }
Glenn L McGrath822e7fd2002-11-24 22:48:20 +0000117
Manuel Novoa III cad53642003-03-19 09:13:01 +0000118 if (!c) {
Denis Vlasenko11152e32008-08-15 19:18:35 +0000119 /* Done. If necessary, update perms on the newly
Manuel Novoa III cad53642003-03-19 09:13:01 +0000120 * created directory. Failure to update here _is_
Denis Vlasenko11152e32008-08-15 19:18:35 +0000121 * an error. */
Denys Vlasenko5cdd1202018-02-06 17:59:32 +0100122 if (mode != -1) {
123 //bb_error_msg("chmod 0%03lo mkdir '%s'", mode, path);
Denys Vlasenko12677702018-02-06 18:01:39 +0100124 if (chmod(path, mode) < 0) {
Denys Vlasenko5cdd1202018-02-06 17:59:32 +0100125 fail_msg = "set permissions of";
126 if (flags & FILEUTILS_IGNORE_CHMOD_ERR) {
127 flags = 0;
128 goto print_err;
129 }
130 break;
Natanael Copa02112d82012-05-22 17:11:46 +0200131 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000132 }
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +0100133 goto ret0;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000134 }
135
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +0100136 /* Remove any inserted nul from the path (recursive mode) */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000137 *s = c;
Denis Vlasenko11152e32008-08-15 19:18:35 +0000138 } /* while (1) */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000139
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +0100140 flags = -1;
Natanael Copa02112d82012-05-22 17:11:46 +0200141 print_err:
142 bb_perror_msg("can't %s directory '%s'", fail_msg, path);
Denys Vlasenkof94c9bf2009-11-29 07:45:33 +0100143 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;
Matt Kraaiceeff732001-06-21 19:41:37 +0000151}