Update date in copyrights
[src/app-framework-main.git] / src / wgtpkg-workdir.c
1 /*
2  Copyright (C) 2015-2019 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 mode_t 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 void put_workdir(int fd, const char *name, size_t length)
53 {
54         /* close the previous directory if any */
55         if (workdirfd >= 0)
56                 close(workdirfd);
57
58         /* copy the name and the fd if existing */
59         if (fd < 0) {
60                 workdir[0] = '.';
61                 workdir[1] = 0;
62                 workdirfd = AT_FDCWD;
63         } else {
64
65                 assert(length < sizeof workdir);
66                 memcpy(workdir, name, 1 + length);
67                 workdirfd = fd;
68         }
69 }
70
71 int set_workdir(const char *name, int create)
72 {
73         int rc, dirfd;
74         size_t length;
75
76         /* check the length */
77         length = strlen(name);
78         if (length >= sizeof workdir) {
79                 ERROR("workdir name too long");
80                 errno = EINVAL;
81                 return -1;
82         }
83
84         /* check if . */
85         if (length == 1 && name[0] == '.') {
86                 put_workdir(AT_FDCWD, name, length);
87                 return 0;
88         }
89
90         /* opens the directory */
91         dirfd = openat(AT_FDCWD, name, O_PATH|O_DIRECTORY|O_RDONLY);
92         if (dirfd < 0) {
93                 if (errno != ENOENT) {
94                         ERROR("error while opening workdir %s: %m", name);
95                         return -1;
96                 }
97                 if (!create) {
98                         ERROR("workdir %s doesn't exist", name);
99                         return -1;
100                 }
101                 rc = mkdirat(AT_FDCWD, name, dirmode);
102                 if (rc) {
103                         ERROR("can't create workdir %s", name);
104                         return -1;
105                 }
106                 dirfd = openat(AT_FDCWD, name, O_PATH|O_DIRECTORY|O_RDONLY);
107                 if (dirfd < 0) {
108                         ERROR("can't open workdir %s", name);
109                         return -1;
110                 }
111         }
112
113         /* record new workdir */
114         put_workdir(dirfd, name, length);
115         return 0;
116 }
117
118 int make_workdir(const char *root, const char *prefix, int reuse)
119 {
120         int i, n, r, l;
121
122         put_workdir(AT_FDCWD, ".", 1);
123
124         n = snprintf(workdir, sizeof workdir, "%s/%s", root, prefix);
125         if (n >= (int)sizeof workdir) {
126                 ERROR("workdir prefix too long");
127                 errno = EINVAL;
128                 return -1;
129         }
130         r = (int)(sizeof workdir) - n;
131
132         /* create a temporary directory */
133         for (i = 0 ; ; i++) {
134                 if (i == INT_MAX) {
135                         ERROR("exhaustion of workdirs");
136                         return -1;
137                 }
138                 l = snprintf(workdir + n, (unsigned)r, "%d", i);
139                 if (l >= r) {
140                         ERROR("computed workdir too long");
141                         errno = EINVAL;
142                         return -1;
143                 }
144                 if (!mkdirat(AT_FDCWD, workdir, dirmode))
145                         break;
146                 if (errno != EEXIST) {
147                         ERROR("error in creation of workdir %s: %m", workdir);
148                         return -1;
149                 }
150                 if (reuse)
151                         break;
152         }
153         workdirfd = openat(AT_FDCWD, workdir, O_RDONLY|O_DIRECTORY);
154         if (workdirfd < 0) {
155                 ERROR("error in onnection to workdir %s: %m", workdir);
156                 rmdir(workdir);
157                 return -1;
158         }
159
160         return 0;
161 }
162
163 int move_workdir(const char *dest, int parents, int force)
164 {
165         int rc;
166         size_t len;
167         struct stat s;
168         char *copy;
169         const char *iter;
170
171         /* check length */
172         if (strlen(dest) >= sizeof workdir) {
173                 ERROR("destination dirname too long");
174                 errno = EINVAL;
175                 return -1;
176         }
177
178         /* if an existing directory exist remove it if force */
179         rc = stat(dest, &s);
180         if (rc == 0) {
181                 if (!S_ISDIR(s.st_mode)) {
182                         ERROR("in move_workdir, can't overwrite regular file %s", dest);
183                         errno = EEXIST;
184                         return -1;
185                 }
186                 if (!force) {
187                         ERROR("in move_workdir, can't overwrite regular file %s", dest);
188                         errno = EEXIST;
189                         return -1;
190                 }
191                 rc = remove_directory_content(dest);
192                 if (rc) {
193                         ERROR("in move_workdir, can't clean dir %s", dest);
194                         return rc;
195                 }
196                 rc = rmdir(dest);
197                 if (rc) {
198                         ERROR("in move_workdir, can't remove dir %s", dest);
199                         return rc;
200                 }
201         } else {
202                 /* check parent of dest */
203                 iter = strrchr(dest, '/');
204                 len = iter ? (size_t)(iter - dest) : 0;
205                 if (len) {
206                         /* is parent existing? */
207                         copy = strndupa(dest, len);
208                         rc = stat(copy, &s);
209                         if (!rc) {
210                                 /* found an entry */
211                                 if (!S_ISDIR(s.st_mode)) {
212                                         ERROR("in move_workdir, '%s' isn't a directory", copy);
213                                         errno = ENOTDIR;
214                                         return -1;
215                                 }
216                         } else if (!parents) {
217                                 /* parent entry not found but not allowed to create it */
218                                 ERROR("in move_workdir, parent directory '%s' not found: %m", copy);
219                                 return -1;
220                         } else if (create_directory(copy, dirmode, 1)) {
221                                 ERROR("in move_workdir, creation of directory %s failed: %m", copy);
222                                 return -1;
223                         }
224                 }
225         }
226
227         /* try to rename now */
228         close(workdirfd);
229         workdirfd = -1;
230         rc = renameat(AT_FDCWD, workdir, AT_FDCWD, dest);
231         if (rc) {
232                 ERROR("in move_workdir, renameat failed %s -> %s: %m", workdir, dest);
233                 return -1;
234         }
235
236         return set_workdir(dest, 0);
237 }
238