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