342df486e14d9739c07354c8b3e443ea8b7c2bba
[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 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 >= 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, 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, len;
160         struct stat s;
161         char *copy;
162         const char *iter;
163
164         /* check length */
165         if (strlen(dest) >= sizeof workdir) {
166                 ERROR("destination dirname too long");
167                 errno = EINVAL;
168                 return -1;
169         }
170
171         /* if an existing directory exist remove it if force */
172         rc = stat(dest, &s);
173         if (rc == 0) {
174                 if (!S_ISDIR(s.st_mode)) {
175                         ERROR("in move_workdir, can't overwrite regular file %s", dest);
176                         errno = EEXIST;
177                         return -1;
178                 }
179                 if (!force) {
180                         ERROR("in move_workdir, can't overwrite regular file %s", dest);
181                         errno = EEXIST;
182                         return -1;
183                 }
184                 rc = remove_directory_content(dest);
185                 if (rc) {
186                         ERROR("in move_workdir, can't clean dir %s", dest);
187                         return rc;
188                 }
189                 rc = rmdir(dest);
190                 if (rc) {
191                         ERROR("in move_workdir, can't remove dir %s", dest);
192                         return rc;
193                 }
194         } else {
195                 /* check parent of dest */
196                 iter = strrchr(dest, '/');
197                 len = iter ? iter - dest : 0;
198                 if (len) {
199                         /* is parent existing? */
200                         copy = strndupa(dest, len);
201                         rc = stat(copy, &s);
202                         if (!rc) {
203                                 /* found an entry */
204                                 if (!S_ISDIR(s.st_mode)) {
205                                         ERROR("in move_workdir, '%s' isn't a directory", copy);
206                                         errno = ENOTDIR;
207                                         return -1;
208                                 }
209                         } else if (!parents) {
210                                 /* parent entry not found but not allowed to create it */
211                                 ERROR("in move_workdir, parent directory '%s' not found: %m", copy);
212                                 return -1;
213                         } else if (create_directory(copy, dirmode, 1)) {
214                                 ERROR("in move_workdir, creation of directory %s failed: %m", copy);
215                                 return -1;
216                         }
217                 }
218         }
219
220         /* try to rename now */
221         close(workdirfd);
222         workdirfd = -1;
223         rc = renameat(AT_FDCWD, workdir, AT_FDCWD, dest);
224         if (rc) {
225                 ERROR("in move_workdir, renameat failed %s -> %s: %m", workdir, dest);
226                 return -1;
227         }
228
229         return set_workdir(dest, 0);
230 }
231