Home | History | Annotate | Download | only in otautil
      1 /*
      2  * Copyright (C) 2007 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 "DirUtil.h"
     18 
     19 #include <stdlib.h>
     20 #include <string.h>
     21 #include <stdio.h>
     22 #include <sys/types.h>
     23 #include <sys/stat.h>
     24 #include <unistd.h>
     25 #include <errno.h>
     26 #include <dirent.h>
     27 #include <limits.h>
     28 
     29 #include <string>
     30 
     31 #include <selinux/label.h>
     32 #include <selinux/selinux.h>
     33 
     34 typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
     35 
     36 static DirStatus
     37 getPathDirStatus(const char *path)
     38 {
     39     struct stat st;
     40     int err;
     41 
     42     err = stat(path, &st);
     43     if (err == 0) {
     44         /* Something's there; make sure it's a directory.
     45          */
     46         if (S_ISDIR(st.st_mode)) {
     47             return DDIR;
     48         }
     49         errno = ENOTDIR;
     50         return DILLEGAL;
     51     } else if (errno != ENOENT) {
     52         /* Something went wrong, or something in the path
     53          * is bad.  Can't do anything in this situation.
     54          */
     55         return DILLEGAL;
     56     }
     57     return DMISSING;
     58 }
     59 
     60 int
     61 dirCreateHierarchy(const char *path, int mode,
     62         const struct utimbuf *timestamp, bool stripFileName,
     63         struct selabel_handle *sehnd)
     64 {
     65     DirStatus ds;
     66 
     67     /* Check for an empty string before we bother
     68      * making any syscalls.
     69      */
     70     if (path[0] == '\0') {
     71         errno = ENOENT;
     72         return -1;
     73     }
     74     // Allocate a path that we can modify; stick a slash on
     75     // the end to make things easier.
     76     std::string cpath = path;
     77     if (stripFileName) {
     78         // Strip everything after the last slash.
     79         size_t pos = cpath.rfind('/');
     80         if (pos == std::string::npos) {
     81             errno = ENOENT;
     82             return -1;
     83         }
     84         cpath.resize(pos + 1);
     85     } else {
     86         // Make sure that the path ends in a slash.
     87         cpath.push_back('/');
     88     }
     89 
     90     /* See if it already exists.
     91      */
     92     ds = getPathDirStatus(cpath.c_str());
     93     if (ds == DDIR) {
     94         return 0;
     95     } else if (ds == DILLEGAL) {
     96         return -1;
     97     }
     98 
     99     /* Walk up the path from the root and make each level.
    100      * If a directory already exists, no big deal.
    101      */
    102     const char *path_start = &cpath[0];
    103     char *p = &cpath[0];
    104     while (*p != '\0') {
    105         /* Skip any slashes, watching out for the end of the string.
    106          */
    107         while (*p != '\0' && *p == '/') {
    108             p++;
    109         }
    110         if (*p == '\0') {
    111             break;
    112         }
    113 
    114         /* Find the end of the next path component.
    115          * We know that we'll see a slash before the NUL,
    116          * because we added it, above.
    117          */
    118         while (*p != '/') {
    119             p++;
    120         }
    121         *p = '\0';
    122 
    123         /* Check this part of the path and make a new directory
    124          * if necessary.
    125          */
    126         ds = getPathDirStatus(path_start);
    127         if (ds == DILLEGAL) {
    128             /* Could happen if some other process/thread is
    129              * messing with the filesystem.
    130              */
    131             return -1;
    132         } else if (ds == DMISSING) {
    133             int err;
    134 
    135             char *secontext = NULL;
    136 
    137             if (sehnd) {
    138                 selabel_lookup(sehnd, &secontext, path_start, mode);
    139                 setfscreatecon(secontext);
    140             }
    141 
    142             err = mkdir(path_start, mode);
    143 
    144             if (secontext) {
    145                 freecon(secontext);
    146                 setfscreatecon(NULL);
    147             }
    148 
    149             if (err != 0) {
    150                 return -1;
    151             }
    152             if (timestamp != NULL && utime(path_start, timestamp)) {
    153                 return -1;
    154             }
    155         }
    156         // else, this directory already exists.
    157 
    158         // Repair the path and continue.
    159         *p = '/';
    160     }
    161     return 0;
    162 }
    163 
    164 int
    165 dirUnlinkHierarchy(const char *path)
    166 {
    167     struct stat st;
    168     DIR *dir;
    169     struct dirent *de;
    170     int fail = 0;
    171 
    172     /* is it a file or directory? */
    173     if (lstat(path, &st) < 0) {
    174         return -1;
    175     }
    176 
    177     /* a file, so unlink it */
    178     if (!S_ISDIR(st.st_mode)) {
    179         return unlink(path);
    180     }
    181 
    182     /* a directory, so open handle */
    183     dir = opendir(path);
    184     if (dir == NULL) {
    185         return -1;
    186     }
    187 
    188     /* recurse over components */
    189     errno = 0;
    190     while ((de = readdir(dir)) != NULL) {
    191         //TODO: don't blow the stack
    192         char dn[PATH_MAX];
    193         if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
    194             continue;
    195         }
    196         snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
    197         if (dirUnlinkHierarchy(dn) < 0) {
    198             fail = 1;
    199             break;
    200         }
    201         errno = 0;
    202     }
    203     /* in case readdir or unlink_recursive failed */
    204     if (fail || errno < 0) {
    205         int save = errno;
    206         closedir(dir);
    207         errno = save;
    208         return -1;
    209     }
    210 
    211     /* close directory handle */
    212     if (closedir(dir) < 0) {
    213         return -1;
    214     }
    215 
    216     /* delete target directory */
    217     return rmdir(path);
    218 }
    219