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