chiark / gitweb /
gpg agent threading bugs: Add some `xxx' comments.
[gnupg2.git] / common / mkdir_p.c
1 /* mkdir_p.c - Create a directory and any missing parents.
2  * Copyright (C) 2015 g10 Code GmbH
3  *
4  * This file is part of GnuPG.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of either
8  *
9  *   - the GNU Lesser General Public License as published by the Free
10  *     Software Foundation; either version 3 of the License, or (at
11  *     your option) any later version.
12  *
13  * or
14  *
15  *   - the GNU General Public License as published by the Free
16  *     Software Foundation; either version 2 of the License, or (at
17  *     your option) any later version.
18  *
19  * or both in parallel, as here.
20  *
21  * This file is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, see <https://www.gnu.org/licenses/>.
28  */
29
30 #include <config.h>
31 #include <sys/stat.h>
32 #include <errno.h>
33 #include <assert.h>
34 #include <stdarg.h>
35
36 #include "util.h"
37 #include "stringhelp.h"
38 #include "logging.h"
39 #include "sysutils.h"
40 #include "mkdir_p.h"
41
42
43 gpg_error_t
44 gnupg_amkdir_p (const char **directory_components)
45 {
46   gpg_error_t err = 0;
47   int count;
48   char **dirs;
49   int i;
50
51   for (count = 0; directory_components[count]; count ++)
52     ;
53
54   /* log_debug ("%s: %d directory components.\n", __func__, count); */
55
56   dirs = xtrycalloc (count, sizeof *dirs);
57   if (!dirs)
58     return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
59
60   for (i = 0; directory_components[i]; i ++)
61     {
62       if (i == 0)
63         dirs[i] = make_filename_try (directory_components[i], NULL);
64       else
65         dirs[i] = make_filename_try (dirs[i-1], directory_components[i], NULL);
66       if (!dirs[i])
67         {
68           err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
69           goto out;
70         }
71
72       /* log_debug ("%s: Directory %d: `%s'.\n", __func__, i, dirs[i]); */
73     }
74
75   for (i = count - 1; i >= 0; i --)
76     {
77       struct stat s;
78
79       /* log_debug ("%s: stat(%s)\n", __func__, dirs[i]); */
80
81       if (!stat (dirs[i], &s))
82         {
83           if ( ! S_ISDIR (s.st_mode))
84             {
85               /* log_debug ("%s: %s exists, but is not a directory!\n", */
86               /*            __func__, dirs[i]); */
87               err = gpg_err_make (default_errsource, GPG_ERR_ENOTDIR);
88               goto out;
89             }
90           else
91             {
92               /* Got a directory.  */
93               /* log_debug ("%s: %s exists and is a directory!\n",  */
94               /*            __func__, dirs[i]); */
95               err = 0;
96               break;
97             }
98         }
99       else if (errno == ENOENT)
100         /* This directory does not exist yet.  Continue walking up the
101            hierarchy.  */
102         {
103           /* log_debug ("%s: %s does not exist!\n", */
104           /*            __func__, dirs[i]); */
105           continue;
106         }
107       else
108         /* Some other error code.  Die.  Note: this could be ENOTDIR
109            (we return this above), which means that a component of the
110            path prefix is not a directory.  */
111         {
112           /* log_debug ("%s: stat(%s) => %s!\n", */
113           /*            __func__, dirs[i], strerror (errno)); */
114           err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
115           goto out;
116         }
117     }
118
119   assert (i >= -1);
120   /* DIRS[I] exists.  Start with the following entry.  */
121   i ++;
122
123   for (; i < count; i ++)
124     {
125       /* log_debug ("Creating directory: %s\n", dirs[i]); */
126
127       if (gnupg_mkdir (dirs[i], "-rwx"))
128         {
129           err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
130           goto out;
131         }
132     }
133
134  out:
135   for (i = 0; i < count; i ++)
136     xfree (dirs[i]);
137   xfree (dirs);
138
139   /* log_debug ("%s: Returning %s\n", __func__, gpg_strerror (rc)); */
140
141   return err;
142 }
143
144
145 gpg_error_t
146 gnupg_mkdir_p (const char *directory_component, ...)
147 {
148   va_list ap;
149   gpg_error_t err = 0;
150   int i;
151   int space = 1;
152   const char **dirs;
153
154   dirs = xtrymalloc (space * sizeof (char *));
155   if (!dirs)
156     return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
157
158   dirs[0] = directory_component;
159
160   va_start (ap, directory_component);
161   for (i = 1; dirs[i - 1]; i ++)
162     {
163       if (i == space)
164         {
165           const char **tmp_dirs;
166
167           space = 2 * space;
168           tmp_dirs = xtryrealloc (dirs, space * sizeof (char *));
169           if (!tmp_dirs)
170             {
171               err = gpg_err_make (default_errsource,
172                                   gpg_err_code_from_syserror ());
173               break;
174             }
175           dirs = tmp_dirs;
176         }
177       dirs[i] = va_arg (ap, char *);
178     }
179   va_end (ap);
180
181   if (!err)
182     err = gnupg_amkdir_p (dirs);
183
184   xfree (dirs);
185
186   return err;
187 }