c0fb6aa6db3e67699066a4dde3223c9ed8140e5b
[src/app-framework-main.git] / src / wgtpkg-workdir.c
1 /*
2  Copyright (C) 2015-2018 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         /* opens the directory */
85         dirfd = openat(AT_FDCWD, name, O_PATH|O_DIRECTORY|O_RDONLY);
86         if (dirfd < 0) {
87                 if (errno != ENOENT) {
88                         ERROR("error while opening workdir %s: %m", name);
89                         return -1;
90                 }
91                 if (!create) {
92                         ERROR("workdir %s doesn't exist", name);
93                         return -1;
94                 }
95                 rc = mkdirat(AT_FDCWD, name, dirmode);
96                 if (rc) {
97                         ERROR("can't create workdir %s", name);
98                         return -1;
99                 }
100                 dirfd = openat(AT_FDCWD, name, O_PATH|O_DIRECTORY|O_RDONLY);
101                 if (dirfd < 0) {
102                         ERROR("can't open workdir %s", name);
103                         return -1;
104                 }
105         }
106
107         /* record new workdir */
108         put_workdir(dirfd, name, length);
109         return 0;
110 }
111
112 int make_workdir(const char *root, const char *prefix, int reuse)
113 {
114         int i, n, r, l;
115
116         put_workdir(AT_FDCWD, ".", 1);
117
118         n = snprintf(workdir, sizeof workdir, "%s/%s", root, prefix);
119         if (n >= (int)sizeof workdir) {
120                 ERROR("workdir prefix too long");
121                 errno = EINVAL;
122                 return -1;
123         }
124         r = (int)(sizeof workdir) - n;
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, (unsigned)r, "%d", i);
133                 if (l >= r) {
134                         ERROR("computed workdir too long");
135                         errno = EINVAL;
136                         return -1;
137                 }
138                 if (!mkdirat(AT_FDCWD, 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 move_workdir(const char *dest, int parents, int force)
158 {
159         int rc;
160         size_t len;
161         struct stat s;
162         char *copy;
163         const char *iter;
164
165         /* check length */
166         if (strlen(dest) >= sizeof workdir) {
167                 ERROR("destination dirname too long");
168                 errno = EINVAL;
169                 return -1;
170         }
171
172         /* if an existing directory exist remove it if force */
173         rc = stat(dest, &s);
174         if (rc == 0) {
175                 if (!S_ISDIR(s.st_mode)) {
176                         ERROR("in move_workdir, can't overwrite regular file %s", dest);
177                         errno = EEXIST;
178                         return -1;
179                 }
180                 if (!force) {
181                         ERROR("in move_workdir, can't overwrite regular file %s", dest);
182                         errno = EEXIST;
183                         return -1;
184                 }
185                 rc = remove_directory_content(dest);
186                 if (rc) {
187                         ERROR("in move_workdir, can't clean dir %s", dest);
188                         return rc;
189                 }
190                 rc = rmdir(dest);
191                 if (rc) {
192                         ERROR("in move_workdir, can't remove dir %s", dest);
193                         return rc;
194                 }
195         } else {
196                 /* check parent of dest */
197                 iter = strrchr(dest, '/');
198                 len = iter ? (size_t)(iter - dest) : 0;
199                 if (len) {
200                         /* is parent existing? */
201                         copy = strndupa(dest, len);
202                         rc = stat(copy, &s);
203                         if (!rc) {
204                                 /* found an entry */
205                                 if (!S_ISDIR(s.st_mode)) {
206                                         ERROR("in move_workdir, '%s' isn't a directory", copy);
207                                         errno = ENOTDIR;
208                                         return -1;
209                                 }
210                         } else if (!parents) {
211                                 /* parent entry not found but not allowed to create it */
212                                 ERROR("in move_workdir, parent directory '%s' not found: %m", copy);
213                                 return -1;
214                         } else if (create_directory(copy, dirmode, 1)) {
215                                 ERROR("in move_workdir, creation of directory %s failed: %m", copy);
216                                 return -1;
217                         }
218                 }
219         }
220
221         /* try to rename now */
222         close(workdirfd);
223         workdirfd = -1;
224         rc = renameat(AT_FDCWD, workdir, AT_FDCWD, dest);
225         if (rc) {
226                 ERROR("in move_workdir, renameat failed %s -> %s: %m", workdir, dest);
227                 return -1;
228         }
229
230         return set_workdir(dest, 0);
231 }
232