1 /* @(#)mkdirs.c 1.5 19/07/31 Copyright 2011-2019 J. Schilling */
2 /*
3 * mkdirs() is the equivalent to "mkdir -p path"
4 * makedirs() allows to create missing direcories before a final
5 * path element if called makedirs(path, mode, TRUE).
6 *
7 * "name" must be a modifyable string.
8 *
9 * Copyright (c) 2011-2019 J. Schilling
10 */
11 /*
12 * The contents of this file are subject to the terms of the
13 * Common Development and Distribution License, Version 1.0 only
14 * (the "License"). You may not use this file except in compliance
15 * with the License.
16 *
17 * See the file CDDL.Schily.txt in this distribution for details.
18 * A copy of the CDDL is also available via the Internet at
19 * http://www.opensource.org/licenses/cddl1.txt
20 *
21 * When distributing Covered Code, include this CDDL HEADER in each
22 * file and include the License file CDDL.Schily.txt from this distribution.
23 */
24
25 /*
26 * Since we need to call stat() and since this is not a predictable call,
27 * we always compile this module in largefile mode.
28 */
29 #define USE_LARGEFILES
30
31 #include <schily/types.h>
32 #include <schily/stat.h>
33 #include <schily/errno.h>
34 #include <schily/string.h>
35 #include <schily/standard.h>
36 #include <schily/schily.h>
37
38 #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST
39 #define is_eexist(err) ((err) == EEXIST || (err) == ENOTEMPTY)
40 #else
41 #define is_eexist(err) ((err) == EEXIST)
42 #endif
43 #if defined(EMISSDIR)
44 #define is_enoent(err) ((err) == ENOENT || (err) == EMISSDIR)
45 #else
46 #define is_enoent(err) ((err) == ENOENT)
47 #endif
48
49 #ifdef __MINGW32__
50 #define mkdir(a, b) mkdir(a)
51 #endif
52
53 EXPORT BOOL makedirs __PR((char *name, mode_t mode, int striplast));
54
55 #ifdef PROTOTYPES
56 EXPORT int
mkdirs(register char * name,mode_t mode)57 mkdirs(register char *name, mode_t mode)
58 #else
59 EXPORT int
60 mkdirs(name, mode)
61 register char *name;
62 mode_t mode;
63 #endif
64 {
65 return (makedirs(name, mode, FALSE));
66 }
67
68 #ifdef PROTOTYPES
69 EXPORT int
makedirs(register char * name,mode_t mode,int striplast)70 makedirs(register char *name, mode_t mode, int striplast)
71 #else
72 EXPORT int
73 makedirs(name, mode, striplast)
74 register char *name;
75 mode_t mode;
76 int striplast;
77 #endif
78 {
79 register char *p;
80 char *ls = NULL;
81 int ret = 0;
82 int err = 0;
83 mode_t mask;
84 struct stat sb;
85
86 if (name == NULL) {
87 seterrno(EFAULT);
88 return (-1);
89 }
90 if (*name == '\0') {
91 seterrno(EINVAL);
92 return (-1);
93 }
94 mask = umask(S_IRWXU|S_IRWXG|S_IRWXO);
95 (void) umask(mask);
96
97 for (p = &name[1]; *p; p++) {
98 if (*p != '/')
99 continue;
100
101 *p = '\0';
102 if (stat(name, &sb) < 0) {
103 if (mkdir(name, mode | S_IRWXU) < 0) {
104 err = geterrno();
105
106 if (!is_eexist(err)) {
107 ret = -1;
108 goto errout;
109 }
110 } else {
111 if (ls && (mode & S_IRWXU) != S_IRWXU) {
112 *ls = '\0';
113 chmod(name, mode & ~mask);
114 *ls = '/';
115 }
116 ls = p;
117 }
118 }
119 *p = '/';
120 }
121 err = 0;
122 if (!striplast)
123 ret = mkdir(name, mode);
124 errout:
125 if (ls && (mode & S_IRWXU) != S_IRWXU) {
126 *ls = '\0';
127 chmod(name, mode & ~mask);
128 *ls = '/';
129 if (err)
130 seterrno(err);
131 }
132 return (ret);
133 }
134