work in progress
[src/app-framework-main.git] / src / wgtpkg-workdir.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16
17 #define _GNU_SOURCE
18
19 #include <unistd.h>
20 #include <string.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <assert.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27
28 #include "verbose.h"
29 #include "wgtpkg.h"
30 #include "utils-dir.h"
31
32 static const int dirmode = 0755;
33 char workdir[PATH_MAX] = { 0, };
34 int workdirfd = -1;
35
36 /* removes the working directory */
37 void remove_workdir()
38 {
39         assert(workdirfd >= 0);
40         remove_directory_content_fd(workdirfd);
41         close(workdirfd);
42         workdirfd = -1;
43         rmdir(workdir);
44         workdir[0] = 0;
45 }
46
47 static int set_real_workdir(const char *name, int create)
48 {
49         int rc, dirfd;
50         size_t length;
51
52         /* check the length */
53         length = strlen(name);
54         if (length >= sizeof workdir) {
55                 ERROR("workdir name too long");
56                 errno = EINVAL;
57                 return -1;
58         }
59
60         /* opens the directory */
61         dirfd = openat(AT_FDCWD, name, O_DIRECTORY|O_RDONLY);
62         if (dirfd < 0) {
63                 if (!create || errno != ENOENT) {
64                         ERROR("no workdir %s", name);
65                         return -1;
66                 }
67                 rc = mkdir(name, dirmode);
68                 if (rc) {
69                         ERROR("can't create workdir %s", name);
70                         return -1;
71                 }
72                 dirfd = open(name, O_PATH|O_DIRECTORY);
73                 if (dirfd < 0) {
74                         ERROR("can't open workdir %s", name);
75                         return -1;
76                 }
77         }
78
79         /* close the previous directory if any */
80         if (workdirfd >= 0)
81                 close(workdirfd);
82         workdirfd = dirfd;
83         memcpy(workdir, name, 1+length);
84         return 0;
85 }
86
87 int set_workdir(const char *name, int create)
88 {
89         char *rp;
90         int rc;
91
92         if (name[0] == '/')
93                 return set_real_workdir(name, create);
94
95         rp = realpath(name, NULL);
96         if (!rp) {
97                 ERROR("realpath failed for %s", name);
98                 return -1;
99         }
100         rc = set_real_workdir(rp, create);
101         free(rp);
102         return rc;
103 }
104
105 static int make_real_workdir_base(const char *root, const char *prefix, int reuse)
106 {
107         int i, n, r, l;
108
109         n = snprintf(workdir, sizeof workdir, "%s/%s", root, prefix);
110         if (n >= sizeof workdir) {
111                 ERROR("workdir prefix too long");
112                 errno = EINVAL;
113                 return -1;
114         }
115         r = (int)(sizeof workdir) - n;
116
117         if (workdirfd >= 0)
118                 close(workdirfd);
119         workdirfd = -1;
120
121         /* create a temporary directory */
122         for (i = 0 ; ; i++) {
123                 if (i == INT_MAX) {
124                         ERROR("exhaustion of workdirs");
125                         return -1;
126                 }
127                 l = snprintf(workdir + n, r, "%d", i);
128                 if (l >= r) {
129                         ERROR("computed workdir too long");
130                         errno = EINVAL;
131                         return -1;
132                 }
133                 if (!mkdir(workdir, dirmode))
134                         break;
135                 if (errno != EEXIST) {
136                         ERROR("error in creation of workdir %s: %m", workdir);
137                         return -1;
138                 }
139                 if (reuse)
140                         break;
141         }
142         workdirfd = openat(AT_FDCWD, workdir, O_RDONLY|O_DIRECTORY);
143         if (workdirfd < 0) {
144                 ERROR("error in onnection to workdir %s: %m", workdir);
145                 rmdir(workdir);
146                 return -1;
147         }
148
149         return 0;
150 }
151
152 int make_workdir_base(const char *root, const char *prefix, int reuse)
153 {
154         char *rp;
155         int rc;
156
157         if (root[0] == '/')
158                 return make_real_workdir_base(root, prefix, reuse);
159
160         rp = realpath(root, NULL);
161         if (!rp) {
162                 ERROR("realpath failed for %s", root);
163                 return -1;
164         }
165         rc = make_real_workdir_base(rp, prefix, reuse);
166         free(rp);
167         return rc;
168 }
169
170 int make_workdir(int reuse)
171 {
172         return make_workdir_base(".", "PACK", reuse);
173 }
174
175 static int move_real_workdir(const char *dest, int parents, int force)
176 {
177         int rc, len;
178         struct stat s;
179         char *copy;
180         const char *iter;
181
182         /* check length */
183         if (strlen(dest) >= sizeof workdir) {
184                 ERROR("destination dirname too long");
185                 errno = EINVAL;
186                 return -1;
187         }
188
189         /* if an existing directory exist remove it if force */
190         rc = stat(dest, &s);
191         if (rc == 0) {
192                 if (!S_ISDIR(s.st_mode)) {
193                         ERROR("in move_real_workdir, can't overwrite regular file %s", dest);
194                         errno = EEXIST;
195                         return -1;
196                 }
197                 if (!force) {
198                         ERROR("in move_real_workdir, can't overwrite regular file %s", dest);
199                         errno = EEXIST;
200                         return -1;
201                 }
202                 rc = remove_directory_content(dest);
203                 if (rc) {
204                         ERROR("in move_real_workdir, can't clean dir %s", dest);
205                         return rc;
206                 }
207                 rc = rmdir(dest);
208                 if (rc) {
209                         ERROR("in move_real_workdir, can't remove dir %s", dest);
210                         return rc;
211                 }
212         } else {
213                 /* check parent of dest */
214                 iter = strrchr(dest, '/');
215                 len = iter ? iter - dest : 0;
216                 if (len) {
217                         /* is parent existing? */
218                         copy = strndupa(dest, len);
219                         rc = stat(copy, &s);
220                         if (!rc) {
221                                 /* found an entry */
222                                 if (!S_ISDIR(s.st_mode)) {
223                                         ERROR("in move_real_workdir, '%s' isn't a directory", copy);
224                                         errno = ENOTDIR;
225                                         return -1;
226                                 }
227                         } else if (!parents) {
228                                 /* parent entry not found but not allowed to create it */
229                                 ERROR("in move_real_workdir, parent directory '%s' not found: %m", copy);
230                                 return -1;
231                         } else if (create_directory(copy, dirmode, 1)) {
232                                 ERROR("in move_real_workdir, creation of directory %s failed: %m", copy);
233                                 return -1;
234                         }
235                 }
236         }
237
238         /* try to rename now */
239         close(workdirfd);
240         workdirfd = -1;
241         rc = renameat(AT_FDCWD, workdir, AT_FDCWD, dest);
242         if (rc) {
243                 ERROR("in move_real_workdir, renameat failed %s -> %s: %m", workdir, dest);
244                 return -1;
245         }
246
247         return set_real_workdir(dest, 0);
248 }
249
250 int move_workdir(const char *dest, int parents, int force)
251 {
252         char *rp;
253         int rc;
254
255         if (dest[0] == '/')
256                 return move_real_workdir(dest, parents, force);
257
258         rp = realpath(dest, NULL);
259         if (!rp) {
260                 ERROR("realpath failed for %s", dest);
261                 return -1;
262         }
263         rc = move_real_workdir(rp, parents, force);
264         free(rp);
265         return rc;
266 }
267