/* vi: set sw=4 ts=4: */
/*
- * Mini make_directory implementation for busybox
+ * parse_mode implementation for busybox
*
- * Copyright (C) 2001 Matt Kraai.
- *
- * Rewriten in 2002
- * Copyright (C) 2002 Glenn McGrath
- * Copyright (C) 2002 Vladimir N. Oleynik
+ * 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.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Mar 5, 2003 Manuel Novoa III
*
- * 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.
+ * This is the main work function for the 'mkdir' applet. As such, it
+ * strives to be SUSv3 compliant in it's behaviour when recursively
+ * making missing parent dirs, and in it's mode setting of the final
+ * directory 'path'.
*
- * 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
+ * To recursively build all missing intermediate directories, make
+ * sure that (flags & FILEUTILS_RECUR) is non-zero. Newly created
+ * intermediate directories will have at least u+wx perms.
*
+ * To set specific permissions on 'path', pass the appropriate 'mode'
+ * val. Otherwise, pass -1 to get default permissions.
*/
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdlib.h>
-
#include "libbb.h"
-/* Create the directory PATH with mode MODE, or the default if MODE is -1.
- * Also create parent directories as necessary if flags contains
- * FILEUTILS_RECUR. */
+/* This function is used from NOFORK applets. It must not allocate anything */
-int make_directory (char *path, long mode, int flags)
+int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
{
- int ret;
-
- /* Calling apps probably should use 0777 instead of -1
- * then we dont need this condition
- */
- if (mode == -1) {
- mode = 0777;
- }
- if (flags == FILEUTILS_RECUR) {
- char *pp = strrchr(path, '/');
- if ((pp) && (pp != path)) {
- *pp = '\0';
- make_directory(path, mode, flags);
- *pp = '/';
+ mode_t cur_mask;
+ mode_t org_mask;
+ const char *fail_msg;
+ char *s;
+ char c;
+ struct stat st;
+
+ /* Happens on bb_make_directory(dirname("no_slashes"),...) */
+ if (LONE_CHAR(path, '.'))
+ return 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 */
+ while (*s) {
+ if (*s == '/') {
+ do {
+ ++s;
+ } while (*s == '/');
+ c = *s; /* Save the current char */
+ *s = '\0'; /* and replace it with nul */
+ break;
+ }
+ ++s;
+ }
}
- }
- ret = mkdir(path, mode);
- if (ret == -1) {
- if ((flags == FILEUTILS_RECUR) && (errno == EEXIST)) {
- ret = 0;
+
+ 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 {
- perror_msg_and_die("Cannot create directory '%s'", path);
+ /* 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);
+ }
}
- }
- return(ret);
+
+ 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 && errno != EISDIR)
+ || !(flags & FILEUTILS_RECUR)
+ || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
+ ) {
+ fail_msg = "create";
+ break;
+ }
+ /* Since the directory exists, don't attempt to change
+ * permissions if it was the full target. Note that
+ * this is not an error condition. */
+ if (!c) {
+ goto ret0;
+ }
+ }
+
+ if (!c) {
+ /* Done. If necessary, update perms on the newly
+ * created directory. Failure to update here _is_
+ * 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;
+ }
+ goto ret0;
+ }
+
+ /* Remove any inserted nul from the path (recursive mode) */
+ *s = c;
+ } /* while (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;
}