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