blob: 5e2ef0b8dd434a77b3d7e905019fea4bc6864764 [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 }
Jeff Sharkey81464032016-01-14 11:53:42 -070058 int owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));
59 int mode_match = ((sb.st_mode & ALL_PERMS) == mode);
60 if (owner_match && mode_match) {
Jeff Sharkey96851942012-08-27 15:03:37 -070061 return 0;
Jeff Sharkeycf94fe12016-01-12 12:42:32 -070062 } else if (allow_fixup) {
Jeff Sharkey96851942012-08-27 15:03:37 -070063 goto fixup;
Jeff Sharkeycf94fe12016-01-12 12:42:32 -070064 } else {
Jeff Sharkey81464032016-01-14 11:53:42 -070065 if (!owner_match) {
66 ALOGE("Expected path %s with owner %d:%d but found %d:%d",
67 path, uid, gid, sb.st_uid, sb.st_gid);
68 return -1;
69 } else {
70 ALOGW("Expected path %s with mode %o but found %o",
71 path, mode, (sb.st_mode & ALL_PERMS));
72 return 0;
73 }
Jeff Sharkey96851942012-08-27 15:03:37 -070074 }
75
76create:
Jeff Sharkeyddb17332012-09-13 14:47:23 -070077 if (TEMP_FAILURE_RETRY(mkdir(path, mode)) == -1) {
Jeff Sharkey489609b2012-09-25 11:10:16 -070078 if (errno != EEXIST) {
79 ALOGE("Failed to mkdir(%s): %s", path, strerror(errno));
80 return -1;
81 }
Jeff Sharkey96851942012-08-27 15:03:37 -070082 }
83
84fixup:
Jeff Sharkeyddb17332012-09-13 14:47:23 -070085 if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) {
86 ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno));
Jeff Sharkey96851942012-08-27 15:03:37 -070087 return -1;
88 }
Jeff Sharkeyddb17332012-09-13 14:47:23 -070089 if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) {
Jeff Sharkey96851942012-08-27 15:03:37 -070090 ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
91 return -1;
92 }
93
94 return 0;
95}
96
Jeff Sharkeycf94fe12016-01-12 12:42:32 -070097int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
98 return fs_prepare_dir_impl(path, mode, uid, gid, 1);
99}
100
101int fs_prepare_dir_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
102 return fs_prepare_dir_impl(path, mode, uid, gid, 0);
103}
104
Jeff Sharkey96851942012-08-27 15:03:37 -0700105int fs_read_atomic_int(const char* path, int* out_value) {
Jeff Sharkey6de70262012-09-13 15:11:42 -0700106 int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));
Jeff Sharkey96851942012-08-27 15:03:37 -0700107 if (fd == -1) {
108 ALOGE("Failed to read %s: %s", path, strerror(errno));
109 return -1;
110 }
111
112 char buf[BUF_SIZE];
Jeff Sharkey6de70262012-09-13 15:11:42 -0700113 if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) {
Jeff Sharkey96851942012-08-27 15:03:37 -0700114 ALOGE("Failed to read %s: %s", path, strerror(errno));
115 goto fail;
116 }
117 if (sscanf(buf, "%d", out_value) != 1) {
118 ALOGE("Failed to parse %s: %s", path, strerror(errno));
119 goto fail;
120 }
121 close(fd);
122 return 0;
123
124fail:
125 close(fd);
126 *out_value = -1;
127 return -1;
128}
129
130int fs_write_atomic_int(const char* path, int value) {
131 char temp[PATH_MAX];
132 if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) {
133 ALOGE("Path too long");
134 return -1;
135 }
136
Jeff Sharkey6de70262012-09-13 15:11:42 -0700137 int fd = TEMP_FAILURE_RETRY(mkstemp(temp));
Jeff Sharkey96851942012-08-27 15:03:37 -0700138 if (fd == -1) {
139 ALOGE("Failed to open %s: %s", temp, strerror(errno));
140 return -1;
141 }
142
143 char buf[BUF_SIZE];
144 int len = snprintf(buf, BUF_SIZE, "%d", value) + 1;
145 if (len > BUF_SIZE) {
146 ALOGE("Value %d too large: %s", value, strerror(errno));
147 goto fail;
148 }
Jeff Sharkey6de70262012-09-13 15:11:42 -0700149 if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) {
Jeff Sharkey96851942012-08-27 15:03:37 -0700150 ALOGE("Failed to write %s: %s", temp, strerror(errno));
151 goto fail;
152 }
153 if (close(fd) == -1) {
154 ALOGE("Failed to close %s: %s", temp, strerror(errno));
155 goto fail_closed;
156 }
157
158 if (rename(temp, path) == -1) {
159 ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno));
160 goto fail_closed;
161 }
162
163 return 0;
164
165fail:
166 close(fd);
167fail_closed:
168 unlink(temp);
169 return -1;
170}
Jeff Sharkey44d63422013-09-12 09:44:48 -0700171
Jeff Sharkey0ee7d8c2013-09-20 17:58:54 -0700172#ifndef __APPLE__
173
Jeff Sharkey44d63422013-09-12 09:44:48 -0700174int fs_mkdirs(const char* path, mode_t mode) {
175 int res = 0;
176 int fd = 0;
177 struct stat sb;
178 char* buf = strdup(path);
179
180 if (*buf != '/') {
181 ALOGE("Relative paths are not allowed: %s", buf);
182 res = -EINVAL;
183 goto done;
184 }
185
186 if ((fd = open("/", 0)) == -1) {
187 ALOGE("Failed to open(/): %s", strerror(errno));
188 res = -errno;
189 goto done;
190 }
191
192 char* segment = buf + 1;
193 char* p = segment;
194 while (*p != '\0') {
195 if (*p == '/') {
196 *p = '\0';
197
198 if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
199 ALOGE("Invalid path: %s", buf);
200 res = -EINVAL;
201 goto done_close;
202 }
203
204 if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
205 if (errno == ENOENT) {
206 /* Nothing there yet; let's create it! */
207 if (mkdirat(fd, segment, mode) != 0) {
208 if (errno == EEXIST) {
209 /* We raced with someone; ignore */
210 } else {
211 ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
212 res = -errno;
213 goto done_close;
214 }
215 }
216 } else {
217 ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
218 res = -errno;
219 goto done_close;
220 }
221 } else {
222 if (S_ISLNK(sb.st_mode)) {
223 ALOGE("Symbolic links are not allowed: %s", buf);
224 res = -ELOOP;
225 goto done_close;
226 }
227 if (!S_ISDIR(sb.st_mode)) {
228 ALOGE("Existing segment not a directory: %s", buf);
229 res = -ENOTDIR;
230 goto done_close;
231 }
232 }
233
234 /* Yay, segment is ready for us to step into */
235 int next_fd;
Nick Kralevich30a86eb2014-06-16 15:32:49 -0700236 if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) {
Jeff Sharkey44d63422013-09-12 09:44:48 -0700237 ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
238 res = -errno;
239 goto done_close;
240 }
241
242 close(fd);
243 fd = next_fd;
244
245 *p = '/';
246 segment = p + 1;
247 }
248 p++;
249 }
250
251done_close:
252 close(fd);
253done:
254 free(buf);
255 return res;
256}
Jeff Sharkey0ee7d8c2013-09-20 17:58:54 -0700257
258#endif