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