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 {
     59     DirStatus ds;
     60 
     61     /* Check for an empty string before we bother
     62      * making any syscalls.
     63      */
     64     if (path[0] == '\0') {
     65         errno = ENOENT;
     66         return -1;
     67     }
     68 
     69     /* Allocate a path that we can modify; stick a slash on
     70      * the end to make things easier.
     71      */
     72     size_t pathLen = strlen(path);
     73     char *cpath = (char *)malloc(pathLen + 2);
     74     if (cpath == NULL) {
     75         errno = ENOMEM;
     76         return -1;
     77     }
     78     memcpy(cpath, path, pathLen);
     79     if (stripFileName) {
     80         /* Strip everything after the last slash.
     81          */
     82         char *c = cpath + pathLen - 1;
     83         while (c != cpath && *c != '/') {
     84             c--;
     85         }
     86         if (c == cpath) {
     87 //xxx test this path
     88             /* No directory component.  Act like the path was empty.
     89              */
     90             errno = ENOENT;
     91             free(cpath);
     92             return -1;
     93         }
     94         c[1] = '\0';    // Terminate after the slash we found.
     95     } else {
     96         /* Make sure that the path ends in a slash.
     97          */
     98         cpath[pathLen] = '/';
     99         cpath[pathLen + 1] = '\0';
    100     }
    101 
    102     /* See if it already exists.
    103      */
    104     ds = getPathDirStatus(cpath);
    105     if (ds == DDIR) {
    106         return 0;
    107     } else if (ds == DILLEGAL) {
    108         return -1;
    109     }
    110 
    111     /* Walk up the path from the root and make each level.
    112      * If a directory already exists, no big deal.
    113      */
    114     char *p = cpath;
    115     while (*p != '\0') {
    116         /* Skip any slashes, watching out for the end of the string.
    117          */
    118         while (*p != '\0' && *p == '/') {
    119             p++;
    120         }
    121         if (*p == '\0') {
    122             break;
    123         }
    124 
    125         /* Find the end of the next path component.
    126          * We know that we'll see a slash before the NUL,
    127          * because we added it, above.
    128          */
    129         while (*p != '/') {
    130             p++;
    131         }
    132         *p = '\0';
    133 
    134         /* Check this part of the path and make a new directory
    135          * if necessary.
    136          */
    137         ds = getPathDirStatus(cpath);
    138         if (ds == DILLEGAL) {
    139             /* Could happen if some other process/thread is
    140              * messing with the filesystem.
    141              */
    142             free(cpath);
    143             return -1;
    144         } else if (ds == DMISSING) {
    145             int err;
    146 
    147             err = mkdir(cpath, mode);
    148             if (err != 0) {
    149                 free(cpath);
    150                 return -1;
    151             }
    152             if (timestamp != NULL && utime(cpath, timestamp)) {
    153                 free(cpath);
    154                 return -1;
    155             }
    156         }
    157         // else, this directory already exists.
    158 
    159         /* Repair the path and continue.
    160          */
    161         *p = '/';
    162     }
    163     free(cpath);
    164 
    165     return 0;
    166 }
    167 
    168 int
    169 dirUnlinkHierarchy(const char *path)
    170 {
    171     struct stat st;
    172     DIR *dir;
    173     struct dirent *de;
    174     int fail = 0;
    175 
    176     /* is it a file or directory? */
    177     if (lstat(path, &st) < 0) {
    178         return -1;
    179     }
    180 
    181     /* a file, so unlink it */
    182     if (!S_ISDIR(st.st_mode)) {
    183         return unlink(path);
    184     }
    185 
    186     /* a directory, so open handle */
    187     dir = opendir(path);
    188     if (dir == NULL) {
    189         return -1;
    190     }
    191 
    192     /* recurse over components */
    193     errno = 0;
    194     while ((de = readdir(dir)) != NULL) {
    195 //TODO: don't blow the stack
    196         char dn[PATH_MAX];
    197         if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
    198             continue;
    199         }
    200         snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
    201         if (dirUnlinkHierarchy(dn) < 0) {
    202             fail = 1;
    203             break;
    204         }
    205         errno = 0;
    206     }
    207     /* in case readdir or unlink_recursive failed */
    208     if (fail || errno < 0) {
    209         int save = errno;
    210         closedir(dir);
    211         errno = save;
    212         return -1;
    213     }
    214 
    215     /* close directory handle */
    216     if (closedir(dir) < 0) {
    217         return -1;
    218     }
    219 
    220     /* delete target directory */
    221     return rmdir(path);
    222 }
    223 
    224 int
    225 dirSetHierarchyPermissions(const char *path,
    226         int uid, int gid, int dirMode, int fileMode)
    227 {
    228     struct stat st;
    229     if (lstat(path, &st)) {
    230         return -1;
    231     }
    232 
    233     /* ignore symlinks */
    234     if (S_ISLNK(st.st_mode)) {
    235         return 0;
    236     }
    237 
    238     /* directories and files get different permissions */
    239     if (chown(path, uid, gid) ||
    240         chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) {
    241         return -1;
    242     }
    243 
    244     /* recurse over directory components */
    245     if (S_ISDIR(st.st_mode)) {
    246         DIR *dir = opendir(path);
    247         if (dir == NULL) {
    248             return -1;
    249         }
    250 
    251         errno = 0;
    252         const struct dirent *de;
    253         while (errno == 0 && (de = readdir(dir)) != NULL) {
    254             if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
    255                 continue;
    256             }
    257 
    258             char dn[PATH_MAX];
    259             snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
    260             if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) {
    261                 errno = 0;
    262             } else if (errno == 0) {
    263                 errno = -1;
    264             }
    265         }
    266 
    267         if (errno != 0) {
    268             int save = errno;
    269             closedir(dir);
    270             errno = save;
    271             return -1;
    272         }
    273 
    274         if (closedir(dir)) {
    275             return -1;
    276         }
    277     }
    278 
    279     return 0;
    280 }
    281