blob: 88c488cba65d6ad4234a7db67627142b16b31781 [file] [log] [blame]
Jeff Sharkey96851942012-08-27 15:03:37 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
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
Jeff Sharkeyddb17332012-09-13 14:47:23 -070017#define LOG_TAG "cutils"
18
Jeff Sharkey44d63422013-09-12 09:44:48 -070019/* These defines are only needed because prebuilt headers are out of date */
20#define __USE_XOPEN2K8 1
21#define _ATFILE_SOURCE 1
22#define _GNU_SOURCE 1
23
Jeff Sharkey96851942012-08-27 15:03:37 -070024#include <cutils/fs.h>
25#include <cutils/log.h>
26
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <unistd.h>
31#include <errno.h>
32#include <string.h>
33#include <limits.h>
Nick Kralevich69ce4892012-10-15 15:51:33 -070034#include <stdlib.h>
Jeff Sharkey44d63422013-09-12 09:44:48 -070035#include <dirent.h>
Jeff Sharkey96851942012-08-27 15:03:37 -070036
37#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
38#define BUF_SIZE 64
39
Jeff Sharkeycf94fe12016-01-12 12:42:32 -070040static int fs_prepare_dir_impl(const char* path, mode_t mode, uid_t uid, gid_t gid,
41 int allow_fixup) {
Jeff Sharkey96851942012-08-27 15:03:37 -070042 // Check if path needs to be created
43 struct stat sb;
Jeff Sharkeyddb17332012-09-13 14:47:23 -070044 if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) {
Jeff Sharkey96851942012-08-27 15:03:37 -070045 if (errno == ENOENT) {
46 goto create;
47 } else {
Jeff Sharkeyddb17332012-09-13 14:47:23 -070048 ALOGE("Failed to lstat(%s): %s", path, strerror(errno));
Jeff Sharkey96851942012-08-27 15:03:37 -070049 return -1;
50 }
51 }
52
53 // Exists, verify status
54 if (!S_ISDIR(sb.st_mode)) {
55 ALOGE("Not a directory: %s", path);
56 return -1;
57 }
58 if (((sb.st_mode & ALL_PERMS) == mode) && (sb.st_uid == uid) && (sb.st_gid == gid)) {
59 return 0;
Jeff Sharkeycf94fe12016-01-12 12:42:32 -070060 } else if (allow_fixup) {
Jeff Sharkey96851942012-08-27 15:03:37 -070061 goto fixup;
Jeff Sharkeycf94fe12016-01-12 12:42:32 -070062 } else {
63 ALOGE("Path %s exists with unexpected permissions", path);
64 return -1;
Jeff Sharkey96851942012-08-27 15:03:37 -070065 }
66
67create:
Jeff Sharkeyddb17332012-09-13 14:47:23 -070068 if (TEMP_FAILURE_RETRY(mkdir(path, mode)) == -1) {
Jeff Sharkey489609b2012-09-25 11:10:16 -070069 if (errno != EEXIST) {
70 ALOGE("Failed to mkdir(%s): %s", path, strerror(errno));
71 return -1;
72 }
Jeff Sharkey96851942012-08-27 15:03:37 -070073 }
74
75fixup:
Jeff Sharkeyddb17332012-09-13 14:47:23 -070076 if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) {
77 ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno));
Jeff Sharkey96851942012-08-27 15:03:37 -070078 return -1;
79 }
Jeff Sharkeyddb17332012-09-13 14:47:23 -070080 if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) {
Jeff Sharkey96851942012-08-27 15:03:37 -070081 ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
82 return -1;
83 }
84
85 return 0;
86}
87
Jeff Sharkeycf94fe12016-01-12 12:42:32 -070088int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
89 return fs_prepare_dir_impl(path, mode, uid, gid, 1);
90}
91
92int fs_prepare_dir_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
93 return fs_prepare_dir_impl(path, mode, uid, gid, 0);
94}
95
Jeff Sharkey96851942012-08-27 15:03:37 -070096int fs_read_atomic_int(const char* path, int* out_value) {
Jeff Sharkey6de70262012-09-13 15:11:42 -070097 int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));
Jeff Sharkey96851942012-08-27 15:03:37 -070098 if (fd == -1) {
99 ALOGE("Failed to read %s: %s", path, strerror(errno));
100 return -1;
101 }
102
103 char buf[BUF_SIZE];
Jeff Sharkey6de70262012-09-13 15:11:42 -0700104 if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) {
Jeff Sharkey96851942012-08-27 15:03:37 -0700105 ALOGE("Failed to read %s: %s", path, strerror(errno));
106 goto fail;
107 }
108 if (sscanf(buf, "%d", out_value) != 1) {
109 ALOGE("Failed to parse %s: %s", path, strerror(errno));
110 goto fail;
111 }
112 close(fd);
113 return 0;
114
115fail:
116 close(fd);
117 *out_value = -1;
118 return -1;
119}
120
121int fs_write_atomic_int(const char* path, int value) {
122 char temp[PATH_MAX];
123 if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) {
124 ALOGE("Path too long");
125 return -1;
126 }
127
Jeff Sharkey6de70262012-09-13 15:11:42 -0700128 int fd = TEMP_FAILURE_RETRY(mkstemp(temp));
Jeff Sharkey96851942012-08-27 15:03:37 -0700129 if (fd == -1) {
130 ALOGE("Failed to open %s: %s", temp, strerror(errno));
131 return -1;
132 }
133
134 char buf[BUF_SIZE];
135 int len = snprintf(buf, BUF_SIZE, "%d", value) + 1;
136 if (len > BUF_SIZE) {
137 ALOGE("Value %d too large: %s", value, strerror(errno));
138 goto fail;
139 }
Jeff Sharkey6de70262012-09-13 15:11:42 -0700140 if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) {
Jeff Sharkey96851942012-08-27 15:03:37 -0700141 ALOGE("Failed to write %s: %s", temp, strerror(errno));
142 goto fail;
143 }
144 if (close(fd) == -1) {
145 ALOGE("Failed to close %s: %s", temp, strerror(errno));
146 goto fail_closed;
147 }
148
149 if (rename(temp, path) == -1) {
150 ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno));
151 goto fail_closed;
152 }
153
154 return 0;
155
156fail:
157 close(fd);
158fail_closed:
159 unlink(temp);
160 return -1;
161}
Jeff Sharkey44d63422013-09-12 09:44:48 -0700162
Jeff Sharkey0ee7d8c2013-09-20 17:58:54 -0700163#ifndef __APPLE__
164
Jeff Sharkey44d63422013-09-12 09:44:48 -0700165int fs_mkdirs(const char* path, mode_t mode) {
166 int res = 0;
167 int fd = 0;
168 struct stat sb;
169 char* buf = strdup(path);
170
171 if (*buf != '/') {
172 ALOGE("Relative paths are not allowed: %s", buf);
173 res = -EINVAL;
174 goto done;
175 }
176
177 if ((fd = open("/", 0)) == -1) {
178 ALOGE("Failed to open(/): %s", strerror(errno));
179 res = -errno;
180 goto done;
181 }
182
183 char* segment = buf + 1;
184 char* p = segment;
185 while (*p != '\0') {
186 if (*p == '/') {
187 *p = '\0';
188
189 if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
190 ALOGE("Invalid path: %s", buf);
191 res = -EINVAL;
192 goto done_close;
193 }
194
195 if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
196 if (errno == ENOENT) {
197 /* Nothing there yet; let's create it! */
198 if (mkdirat(fd, segment, mode) != 0) {
199 if (errno == EEXIST) {
200 /* We raced with someone; ignore */
201 } else {
202 ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
203 res = -errno;
204 goto done_close;
205 }
206 }
207 } else {
208 ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
209 res = -errno;
210 goto done_close;
211 }
212 } else {
213 if (S_ISLNK(sb.st_mode)) {
214 ALOGE("Symbolic links are not allowed: %s", buf);
215 res = -ELOOP;
216 goto done_close;
217 }
218 if (!S_ISDIR(sb.st_mode)) {
219 ALOGE("Existing segment not a directory: %s", buf);
220 res = -ENOTDIR;
221 goto done_close;
222 }
223 }
224
225 /* Yay, segment is ready for us to step into */
226 int next_fd;
Nick Kralevich30a86eb2014-06-16 15:32:49 -0700227 if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) {
Jeff Sharkey44d63422013-09-12 09:44:48 -0700228 ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
229 res = -errno;
230 goto done_close;
231 }
232
233 close(fd);
234 fd = next_fd;
235
236 *p = '/';
237 segment = p + 1;
238 }
239 p++;
240 }
241
242done_close:
243 close(fd);
244done:
245 free(buf);
246 return res;
247}
Jeff Sharkey0ee7d8c2013-09-20 17:58:54 -0700248
249#endif