*
* Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
/* Mar 5, 2003 Manuel Novoa III
* val. Otherwise, pass -1 to get default permissions.
*/
-#include <errno.h>
-#include <unistd.h>
#include "libbb.h"
-int bb_make_directory (char *path, long mode, int flags)
+/* This function is used from NOFORK applets. It must not allocate anything */
+
+int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
{
- mode_t mask;
+ mode_t cur_mask;
+ mode_t org_mask;
const char *fail_msg;
- char *s = path;
+ char *s;
char c;
+ struct stat st;
- mask = umask(0);
- umask(mask & ~0300);
+ /* Happens on bb_make_directory(dirname("no_slashes"),...) */
+ if (LONE_CHAR(path, '.'))
+ return 0;
- do {
- c = 0;
+ org_mask = cur_mask = (mode_t)-1L;
+ s = path;
+ while (1) {
+ c = '\0';
- if (flags & FILEUTILS_RECUR) { /* Get the parent. */
- /* Bypass leading non-'/'s and then subsequent '/'s. */
+ if (flags & FILEUTILS_RECUR) { /* Get the parent */
+ /* Bypass leading non-'/'s and then subsequent '/'s */
while (*s) {
if (*s == '/') {
do {
++s;
} while (*s == '/');
- c = *s; /* Save the current char */
- *s = 0; /* and replace it with nul. */
+ c = *s; /* Save the current char */
+ *s = '\0'; /* and replace it with nul */
break;
}
++s;
}
}
+ if (c != '\0') {
+ /* Intermediate dirs: must have wx for user */
+ if (cur_mask == (mode_t)-1L) { /* wasn't done yet? */
+ mode_t new_mask;
+ org_mask = umask(0);
+ cur_mask = 0;
+ /* Clear u=wx in umask - this ensures
+ * they won't be cleared on mkdir */
+ new_mask = (org_mask & ~(mode_t)0300);
+ //bb_error_msg("org_mask:%o cur_mask:%o", org_mask, new_mask);
+ if (new_mask != cur_mask) {
+ cur_mask = new_mask;
+ umask(new_mask);
+ }
+ }
+ } else {
+ /* Last component: uses original umask */
+ //bb_error_msg("1 org_mask:%o", org_mask);
+ if (org_mask != cur_mask) {
+ cur_mask = org_mask;
+ umask(org_mask);
+ }
+ }
+
if (mkdir(path, 0777) < 0) {
/* If we failed for any other reason than the directory
- * already exists, output a diagnostic and return -1.*/
- if (errno != EEXIST) {
+ * already exists, output a diagnostic and return -1 */
+ if ((errno != EEXIST && errno != EISDIR)
+ || !(flags & FILEUTILS_RECUR)
+ || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
+ ) {
fail_msg = "create";
- umask(mask);
break;
}
/* Since the directory exists, don't attempt to change
* permissions if it was the full target. Note that
- * this is not an error conditon. */
+ * this is not an error condition. */
if (!c) {
- umask(mask);
- return 0;
+ goto ret0;
}
}
if (!c) {
- /* Done. If necessary, updated perms on the newly
+ /* Done. If necessary, update perms on the newly
* created directory. Failure to update here _is_
- * an error.*/
- umask(mask);
- if ((mode != -1) && (chmod(path, mode) < 0)){
+ * an error. */
+ if ((mode != -1) && (chmod(path, mode) < 0)) {
fail_msg = "set permissions of";
+ if (flags & FILEUTILS_IGNORE_CHMOD_ERR) {
+ flags = 0;
+ goto print_err;
+ }
break;
}
- return 0;
+ goto ret0;
}
- /* Remove any inserted nul from the path (recursive mode). */
+ /* Remove any inserted nul from the path (recursive mode) */
*s = c;
+ } /* while (1) */
- } while (1);
-
- bb_perror_msg ("Cannot %s directory `%s'", fail_msg, path);
- return -1;
+ flags = -1;
+ print_err:
+ bb_perror_msg("can't %s directory '%s'", fail_msg, path);
+ goto ret;
+ ret0:
+ flags = 0;
+ ret:
+ //bb_error_msg("2 org_mask:%o", org_mask);
+ if (org_mask != cur_mask)
+ umask(org_mask);
+ return flags;
}