aef0a65f16c3d34a4e2446ab93538ddd389b2104
[src/app-framework-main.git] / src / utils-dir.c
1 /*
2  Copyright 2015, 2016, 2017 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 <string.h>
22 #include <dirent.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28
29 #include "utils-dir.h"
30
31 static int clean_dirfd(int dirfd)
32 {
33         int rc;
34         DIR *dir;
35         struct dirent *ent;
36         struct {
37                 struct dirent entry;
38                 char spare[PATH_MAX];
39         } entry;
40
41         dir = fdopendir(dirfd);
42         if (dir == NULL) {
43                 close(dirfd);
44                 return -1;
45         }
46         for (;;) {
47                 rc = -1;
48                 if (readdir_r(dir, &entry.entry, &ent) != 0)
49                         goto error;
50                 if (ent == NULL)
51                         break;
52                 if (ent->d_name[0] == '.' && (ent->d_name[1] == 0
53                                 || (ent->d_name[1] == '.' && ent->d_name[2] == 0)))
54                         continue;
55                 rc = unlinkat(dirfd, ent->d_name, 0);
56                 if (!rc)
57                         continue;
58                 if (errno != EISDIR)
59                         goto error;
60                 rc = openat(dirfd, ent->d_name, O_DIRECTORY|O_RDONLY);
61                 if (rc < 0)
62                         goto error;
63                 rc = clean_dirfd(rc);
64                 if (rc)
65                         goto error;
66                 rc = unlinkat(dirfd, ent->d_name, AT_REMOVEDIR);
67                 if (rc)
68                         goto error;
69         }
70         rc = 0;
71 error:
72         closedir(dir);
73         return rc;
74 }
75
76 /* removes recursively the content of a directory */
77 int remove_directory_content_fd(int dirfd)
78 {
79         dirfd = dup(dirfd);
80         return dirfd < 0 ? dirfd : clean_dirfd(dirfd);
81 }
82
83 /* removes recursively the content of a directory */
84 int remove_directory_content_at(int dirfd, const char *directory)
85 {
86         int fd, rc;
87
88         fd = openat(dirfd, directory, O_DIRECTORY|O_RDONLY);
89         if (fd < 0)
90                 return fd;
91         rc = remove_directory_content_fd(fd);
92         return rc;
93 }
94
95
96 int remove_directory_content(const char *directory)
97 {
98         return remove_directory_content_at(AT_FDCWD, directory);
99 }
100
101 /* removes the working directory */
102 int remove_directory(const char *directory, int force)
103 {
104         int rc;
105         rc = force ? remove_directory_content(directory) : 0;
106         return rc ? rc : rmdir(directory);
107 }
108
109 int remove_directory_at(int dirfd, const char *directory, int force)
110 {
111         int rc;
112         rc = force ? remove_directory_content_at(dirfd, directory) : 0;
113         return rc ? rc : unlinkat(dirfd, directory, AT_REMOVEDIR);
114 }
115
116 /* create a directory */
117 int create_directory_at(int dirfd, const char *directory, mode_t mode, int mkparents)
118 {
119         int rc;
120         size_t len, l;
121         char *copy;
122         const char *iter;
123
124         rc = mkdirat(dirfd, directory, mode);
125         if (!rc || errno != ENOENT)
126                 return rc;
127
128         /* check parent of dest */
129         iter = strrchr(directory, '/');
130         len = iter ? (size_t)(iter - directory) : 0;
131         if (!len)
132                 return rc;
133         copy = strndupa(directory, len);
134
135         /* backward loop */
136         l = len;
137         rc = mkdirat(dirfd, copy, mode);
138         while(rc) {
139                 if (errno != ENOENT)
140                         return rc;
141                 while (l && copy[l] != '/')
142                         l--;
143                 if (l == 0)
144                         return rc;
145                 copy[l] = 0;
146                 rc = mkdirat(dirfd, copy, mode);
147         }
148         /* forward loop */
149         while(l < len) {
150                 copy[l] = '/';
151                 while (copy[++l]);
152                 rc = mkdirat(dirfd, copy, mode);
153                 if (rc)
154                         return rc;
155         }
156
157         /* finalize */
158         return mkdirat(dirfd, directory, mode);
159 }
160
161 int create_directory(const char *directory, mode_t mode, int mkparents)
162 {
163         return create_directory_at(AT_FDCWD, directory, mode, mkparents);
164 }
165