1 /*
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 
17 #define LOG_TAG "cutils"
18 
19 /* 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 
24 #include <dirent.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 
35 #include <cutils/fs.h>
36 #include <log/log.h>
37 
38 #define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
39 #define BUF_SIZE 64
40 
fs_prepare_path_impl(const char * path,mode_t mode,uid_t uid,gid_t gid,int allow_fixup,int prepare_as_dir)41 static int fs_prepare_path_impl(const char* path, mode_t mode, uid_t uid, gid_t gid,
42         int allow_fixup, int prepare_as_dir) {
43     // Check if path needs to be created
44     struct stat sb;
45     int create_result = -1;
46     if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) {
47         if (errno == ENOENT) {
48             goto create;
49         } else {
50             ALOGE("Failed to lstat(%s): %s", path, strerror(errno));
51             return -1;
52         }
53     }
54 
55     // Exists, verify status
56     int type_ok = prepare_as_dir ? S_ISDIR(sb.st_mode) : S_ISREG(sb.st_mode);
57     if (!type_ok) {
58         ALOGE("Not a %s: %s", (prepare_as_dir ? "directory" : "regular file"), path);
59         return -1;
60     }
61 
62     int owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));
63     int mode_match = ((sb.st_mode & ALL_PERMS) == mode);
64     if (owner_match && mode_match) {
65         return 0;
66     } else if (allow_fixup) {
67         goto fixup;
68     } else {
69         if (!owner_match) {
70             ALOGE("Expected path %s with owner %d:%d but found %d:%d",
71                     path, uid, gid, sb.st_uid, sb.st_gid);
72             return -1;
73         } else {
74             ALOGW("Expected path %s with mode %o but found %o",
75                     path, mode, (sb.st_mode & ALL_PERMS));
76             return 0;
77         }
78     }
79 
80 create:
81     create_result = prepare_as_dir
82         ? TEMP_FAILURE_RETRY(mkdir(path, mode))
83         : TEMP_FAILURE_RETRY(open(path, O_CREAT | O_CLOEXEC | O_NOFOLLOW | O_RDONLY, 0644));
84     if (create_result == -1) {
85         if (errno != EEXIST) {
86             ALOGE("Failed to %s(%s): %s",
87                     (prepare_as_dir ? "mkdir" : "open"), path, strerror(errno));
88             return -1;
89         }
90     } else if (!prepare_as_dir) {
91         // For regular files we need to make sure we close the descriptor
92         if (close(create_result) == -1) {
93             ALOGW("Failed to close file after create %s: %s", path, strerror(errno));
94         }
95     }
96 fixup:
97     if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) {
98         ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno));
99         return -1;
100     }
101     if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) {
102         ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
103         return -1;
104     }
105 
106     return 0;
107 }
108 
fs_prepare_dir(const char * path,mode_t mode,uid_t uid,gid_t gid)109 int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
110     return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 1, /*prepare_as_dir*/ 1);
111 }
112 
fs_prepare_dir_strict(const char * path,mode_t mode,uid_t uid,gid_t gid)113 int fs_prepare_dir_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
114     return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 1);
115 }
116 
fs_prepare_file_strict(const char * path,mode_t mode,uid_t uid,gid_t gid)117 int fs_prepare_file_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
118     return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 0);
119 }
120 
fs_read_atomic_int(const char * path,int * out_value)121 int fs_read_atomic_int(const char* path, int* out_value) {
122     int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));
123     if (fd == -1) {
124         ALOGE("Failed to read %s: %s", path, strerror(errno));
125         return -1;
126     }
127 
128     char buf[BUF_SIZE];
129     if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) {
130         ALOGE("Failed to read %s: %s", path, strerror(errno));
131         goto fail;
132     }
133     if (sscanf(buf, "%d", out_value) != 1) {
134         ALOGE("Failed to parse %s: %s", path, strerror(errno));
135         goto fail;
136     }
137     close(fd);
138     return 0;
139 
140 fail:
141     close(fd);
142     *out_value = -1;
143     return -1;
144 }
145 
fs_write_atomic_int(const char * path,int value)146 int fs_write_atomic_int(const char* path, int value) {
147     char temp[PATH_MAX];
148     if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) {
149         ALOGE("Path too long");
150         return -1;
151     }
152 
153     int fd = TEMP_FAILURE_RETRY(mkstemp(temp));
154     if (fd == -1) {
155         ALOGE("Failed to open %s: %s", temp, strerror(errno));
156         return -1;
157     }
158 
159     char buf[BUF_SIZE];
160     int len = snprintf(buf, BUF_SIZE, "%d", value) + 1;
161     if (len > BUF_SIZE) {
162         ALOGE("Value %d too large: %s", value, strerror(errno));
163         goto fail;
164     }
165     if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) {
166         ALOGE("Failed to write %s: %s", temp, strerror(errno));
167         goto fail;
168     }
169     if (close(fd) == -1) {
170         ALOGE("Failed to close %s: %s", temp, strerror(errno));
171         goto fail_closed;
172     }
173 
174     if (rename(temp, path) == -1) {
175         ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno));
176         goto fail_closed;
177     }
178 
179     return 0;
180 
181 fail:
182     close(fd);
183 fail_closed:
184     unlink(temp);
185     return -1;
186 }
187 
188 #ifndef __APPLE__
189 
fs_mkdirs(const char * path,mode_t mode)190 int fs_mkdirs(const char* path, mode_t mode) {
191     int res = 0;
192     int fd = 0;
193     struct stat sb;
194     char* buf = strdup(path);
195 
196     if (*buf != '/') {
197         ALOGE("Relative paths are not allowed: %s", buf);
198         res = -EINVAL;
199         goto done;
200     }
201 
202     if ((fd = open("/", 0)) == -1) {
203         ALOGE("Failed to open(/): %s", strerror(errno));
204         res = -errno;
205         goto done;
206     }
207 
208     char* segment = buf + 1;
209     char* p = segment;
210     while (*p != '\0') {
211         if (*p == '/') {
212             *p = '\0';
213 
214             if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
215                 ALOGE("Invalid path: %s", buf);
216                 res = -EINVAL;
217                 goto done_close;
218             }
219 
220             if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
221                 if (errno == ENOENT) {
222                     /* Nothing there yet; let's create it! */
223                     if (mkdirat(fd, segment, mode) != 0) {
224                         if (errno == EEXIST) {
225                             /* We raced with someone; ignore */
226                         } else {
227                             ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
228                             res = -errno;
229                             goto done_close;
230                         }
231                     }
232                 } else {
233                     ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
234                     res = -errno;
235                     goto done_close;
236                 }
237             } else {
238                 if (S_ISLNK(sb.st_mode)) {
239                     ALOGE("Symbolic links are not allowed: %s", buf);
240                     res = -ELOOP;
241                     goto done_close;
242                 }
243                 if (!S_ISDIR(sb.st_mode)) {
244                     ALOGE("Existing segment not a directory: %s", buf);
245                     res = -ENOTDIR;
246                     goto done_close;
247                 }
248             }
249 
250             /* Yay, segment is ready for us to step into */
251             int next_fd;
252             if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) {
253                 ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
254                 res = -errno;
255                 goto done_close;
256             }
257 
258             close(fd);
259             fd = next_fd;
260 
261             *p = '/';
262             segment = p + 1;
263         }
264         p++;
265     }
266 
267 done_close:
268     close(fd);
269 done:
270     free(buf);
271     return res;
272 }
273 
274 #endif
275