af0bcf0eebf25855f5c83a1b4063b1fb08a2e960
[src/app-framework-main.git] / src / utils-dir.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16
17 #define _GNU_SOURCE
18
19 #include <string.h>
20 #include <dirent.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26
27 #include "utils-dir.h"
28
29 static int clean_dirfd(int dirfd)
30 {
31         int rc;
32         DIR *dir;
33         struct dirent *ent;
34         struct {
35                 struct dirent entry;
36                 char spare[PATH_MAX];
37         } entry;
38
39         dir = fdopendir(dirfd);
40         if (dir == NULL) {
41                 close(dirfd);
42                 return -1;
43         }
44         for (;;) {
45                 rc = -1;
46                 if (readdir_r(dir, &entry.entry, &ent) != 0)
47                         goto error;
48                 if (ent == NULL)
49                         break;
50                 if (ent->d_name[0] == '.' && (ent->d_name[1] == 0
51                                 || (ent->d_name[1] == '.' && ent->d_name[2] == 0)))
52                         continue;
53                 rc = unlinkat(dirfd, ent->d_name, 0);
54                 if (!rc)
55                         continue;
56                 if (errno != EISDIR)
57                         goto error;
58                 rc = openat(dirfd, ent->d_name, O_DIRECTORY|O_RDONLY);
59                 if (rc < 0)
60                         goto error;
61                 rc = clean_dirfd(rc);
62                 if (rc)
63                         goto error;
64                 rc = unlinkat(dirfd, ent->d_name, AT_REMOVEDIR);
65                 if (rc)
66                         goto error;
67         }
68         rc = 0;
69 error:
70         closedir(dir);
71         return rc;
72 }
73
74 /* removes recursively the content of a directory */
75 int remove_directory_content_fd(int dirfd)
76 {
77         dirfd = dup(dirfd);
78         return dirfd < 0 ? dirfd : clean_dirfd(dirfd);
79 }
80
81 /* removes recursively the content of a directory */
82 int remove_directory_content_at(int dirfd, const char *directory)
83 {
84         int fd, rc;
85
86         fd = openat(dirfd, directory, O_DIRECTORY|O_RDONLY);
87         if (fd < 0)
88                 return fd;
89         rc = remove_directory_content_fd(fd);
90         return rc;
91 }
92
93
94 int remove_directory_content(const char *directory)
95 {
96         return remove_directory_content_at(AT_FDCWD, directory);
97 }
98
99 /* removes the working directory */
100 int remove_directory(const char *directory, int force)
101 {
102         int rc;
103         rc = force ? remove_directory_content(directory) : 0;
104         return rc ? rc : rmdir(directory);
105 }
106
107 int remove_directory_at(int dirfd, const char *directory, int force)
108 {
109         int rc;
110         rc = force ? remove_directory_content_at(dirfd, directory) : 0;
111         return rc ? rc : unlinkat(dirfd, directory, AT_REMOVEDIR);
112 }
113
114 /* create a directory */
115 int create_directory_at(int dirfd, const char *directory, int mode, int mkparents)
116 {
117         int rc, len, l;
118         char *copy;
119         const char *iter;
120
121         rc = mkdirat(dirfd, directory, mode);
122         if (!rc || errno != ENOENT)
123                 return rc;
124
125         /* check parent of dest */
126         iter = strrchr(directory, '/');
127         len = iter ? iter - directory : 0;
128         if (!len)
129                 return rc;
130         copy = strndupa(directory, len);
131
132         /* backward loop */
133         l = len;
134         rc = mkdirat(dirfd, copy, mode);
135         while(rc) {
136                 if (errno != ENOENT)
137                         return rc;
138                 while (l && copy[l] != '/')
139                         l--;
140                 if (l == 0)
141                         return rc;
142                 copy[l] = 0;
143                 rc = mkdirat(dirfd, copy, mode);
144         }
145         /* forward loop */
146         while(l < len) {
147                 copy[l] = '/';
148                 while (copy[++l]);
149                 rc = mkdirat(dirfd, copy, mode);
150                 if (rc)
151                         return rc;
152         }
153
154         /* finalize */
155         return mkdirat(dirfd, directory, mode);
156 }
157
158 int create_directory(const char *directory, int mode, int mkparents)
159 {
160         return create_directory_at(AT_FDCWD, directory, mode, mkparents);
161 }
162