31bfd8a442ee402004d20b1f4fec4bd860b6c63e
[src/app-framework-main.git] / src / utils-dir.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 <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, int mode, int mkparents)
118 {
119         int rc, 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 ? 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, int mode, int mkparents)
161 {
162         return create_directory_at(AT_FDCWD, directory, mode, mkparents);
163 }
164