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