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