Path: blob/main/sys/contrib/openzfs/lib/libspl/mkdirp.c
48378 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright 2008 Sun Microsystems, Inc. All rights reserved.24* Use is subject to license terms.25*/2627/* Copyright (c) 1988 AT&T */28/* All Rights Reserved */2930/*31* Creates directory and it's parents if the parents do not32* exist yet.33*34* Returns -1 if fails for reasons other than non-existing35* parents.36* Does NOT simplify pathnames with . or .. in them.37*/3839#include <sys/types.h>40#include <libgen.h>41#include <stdlib.h>42#include <unistd.h>43#include <errno.h>44#include <string.h>45#include <sys/stat.h>4647static char *simplify(const char *str);4849int50mkdirp(const char *d, mode_t mode)51{52char *endptr, *ptr, *slash, *str;5354str = simplify(d);5556/* If space couldn't be allocated for the simplified names, return. */5758if (str == NULL)59return (-1);6061/* Try to make the directory */6263if (mkdir(str, mode) == 0) {64free(str);65return (0);66}67if (errno != ENOENT) {68free(str);69return (-1);70}71endptr = strrchr(str, '\0');72slash = strrchr(str, '/');7374/* Search upward for the non-existing parent */7576while (slash != NULL) {7778ptr = slash;79*ptr = '\0';8081/* If reached an existing parent, break */8283if (access(str, F_OK) == 0)84break;8586/* If non-existing parent */8788else {89slash = strrchr(str, '/');9091/* If under / or current directory, make it. */9293if (slash == NULL || slash == str) {94if (mkdir(str, mode) != 0 && errno != EEXIST) {95free(str);96return (-1);97}98break;99}100}101}102103/* Create directories starting from upmost non-existing parent */104105while ((ptr = strchr(str, '\0')) != endptr) {106*ptr = '/';107if (mkdir(str, mode) != 0 && errno != EEXIST) {108/*109* If the mkdir fails because str already110* exists (EEXIST), then str has the form111* "existing-dir/..", and this is really112* ok. (Remember, this loop is creating the113* portion of the path that didn't exist)114*/115free(str);116return (-1);117}118}119free(str);120return (0);121}122123/*124* simplify - given a pathname, simplify that path by removing125* duplicate contiguous slashes.126*127* A simplified copy of the argument is returned to the128* caller, or NULL is returned on error.129*130* The caller should handle error reporting based upon the131* returned value, and should free the returned value,132* when appropriate.133*/134135static char *136simplify(const char *str)137{138int i;139size_t mbPathlen; /* length of multi-byte path */140size_t wcPathlen; /* length of wide-character path */141wchar_t *wptr; /* scratch pointer */142wchar_t *wcPath; /* wide-character version of the path */143char *mbPath; /* The copy fo the path to be returned */144145/*146* bail out if there is nothing there.147*/148149if (!str) {150errno = ENOENT;151return (NULL);152}153154/*155* Get a copy of the argument.156*/157158if ((mbPath = strdup(str)) == NULL) {159return (NULL);160}161162/*163* convert the multi-byte version of the path to a164* wide-character rendering, for doing our figuring.165*/166167mbPathlen = strlen(mbPath);168169if ((wcPath = calloc(mbPathlen+1, sizeof (wchar_t))) == NULL) {170free(mbPath);171return (NULL);172}173174if ((wcPathlen = mbstowcs(wcPath, mbPath, mbPathlen)) == (size_t)-1) {175free(mbPath);176free(wcPath);177return (NULL);178}179180/*181* remove duplicate slashes first ("//../" -> "/")182*/183184for (wptr = wcPath, i = 0; i < wcPathlen; i++) {185*wptr++ = wcPath[i];186187if (wcPath[i] == '/') {188i++;189190while (wcPath[i] == '/') {191i++;192}193194i--;195}196}197198*wptr = '\0';199200/*201* now convert back to the multi-byte format.202*/203204if (wcstombs(mbPath, wcPath, mbPathlen) == (size_t)-1) {205free(mbPath);206free(wcPath);207return (NULL);208}209210free(wcPath);211return (mbPath);212}213214215