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