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