Home | History | Annotate | Download | only in libcutils
      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 
     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 
    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 
    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 
    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 
    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 
    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 
    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