diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-04 04:28:42 +0100 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-04 04:28:42 +0100 |
commit | c24a8e688a6312764254beac2b2520bb0c5e998d (patch) | |
tree | 7df23711566b358047301d8413ee399105546e8b /minzip/DirUtil.c | |
parent | auto import from //depot/cupcake/@135843 (diff) | |
download | android_bootable_recovery-c24a8e688a6312764254beac2b2520bb0c5e998d.tar android_bootable_recovery-c24a8e688a6312764254beac2b2520bb0c5e998d.tar.gz android_bootable_recovery-c24a8e688a6312764254beac2b2520bb0c5e998d.tar.bz2 android_bootable_recovery-c24a8e688a6312764254beac2b2520bb0c5e998d.tar.lz android_bootable_recovery-c24a8e688a6312764254beac2b2520bb0c5e998d.tar.xz android_bootable_recovery-c24a8e688a6312764254beac2b2520bb0c5e998d.tar.zst android_bootable_recovery-c24a8e688a6312764254beac2b2520bb0c5e998d.zip |
Diffstat (limited to 'minzip/DirUtil.c')
-rw-r--r-- | minzip/DirUtil.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c new file mode 100644 index 000000000..20c89cd6f --- /dev/null +++ b/minzip/DirUtil.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <dirent.h> +#include <limits.h> + +#include "DirUtil.h" + +typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus; + +static DirStatus +getPathDirStatus(const char *path) +{ + struct stat st; + int err; + + err = stat(path, &st); + if (err == 0) { + /* Something's there; make sure it's a directory. + */ + if (S_ISDIR(st.st_mode)) { + return DDIR; + } + errno = ENOTDIR; + return DILLEGAL; + } else if (errno != ENOENT) { + /* Something went wrong, or something in the path + * is bad. Can't do anything in this situation. + */ + return DILLEGAL; + } + return DMISSING; +} + +int +dirCreateHierarchy(const char *path, int mode, + const struct utimbuf *timestamp, bool stripFileName) +{ + DirStatus ds; + + /* Check for an empty string before we bother + * making any syscalls. + */ + if (path[0] == '\0') { + errno = ENOENT; + return -1; + } + + /* Allocate a path that we can modify; stick a slash on + * the end to make things easier. + */ + size_t pathLen = strlen(path); + char *cpath = (char *)malloc(pathLen + 2); + if (cpath == NULL) { + errno = ENOMEM; + return -1; + } + memcpy(cpath, path, pathLen); + if (stripFileName) { + /* Strip everything after the last slash. + */ + char *c = cpath + pathLen - 1; + while (c != cpath && *c != '/') { + c--; + } + if (c == cpath) { +//xxx test this path + /* No directory component. Act like the path was empty. + */ + errno = ENOENT; + free(cpath); + return -1; + } + c[1] = '\0'; // Terminate after the slash we found. + } else { + /* Make sure that the path ends in a slash. + */ + cpath[pathLen] = '/'; + cpath[pathLen + 1] = '\0'; + } + + /* See if it already exists. + */ + ds = getPathDirStatus(cpath); + if (ds == DDIR) { + return 0; + } else if (ds == DILLEGAL) { + return -1; + } + + /* Walk up the path from the root and make each level. + * If a directory already exists, no big deal. + */ + char *p = cpath; + while (*p != '\0') { + /* Skip any slashes, watching out for the end of the string. + */ + while (*p != '\0' && *p == '/') { + p++; + } + if (*p == '\0') { + break; + } + + /* Find the end of the next path component. + * We know that we'll see a slash before the NUL, + * because we added it, above. + */ + while (*p != '/') { + p++; + } + *p = '\0'; + + /* Check this part of the path and make a new directory + * if necessary. + */ + ds = getPathDirStatus(cpath); + if (ds == DILLEGAL) { + /* Could happen if some other process/thread is + * messing with the filesystem. + */ + free(cpath); + return -1; + } else if (ds == DMISSING) { + int err; + + err = mkdir(cpath, mode); + if (err != 0) { + free(cpath); + return -1; + } + if (timestamp != NULL && utime(cpath, timestamp)) { + free(cpath); + return -1; + } + } + // else, this directory already exists. + + /* Repair the path and continue. + */ + *p = '/'; + } + free(cpath); + + return 0; +} + +int +dirUnlinkHierarchy(const char *path) +{ + struct stat st; + DIR *dir; + struct dirent *de; + int fail = 0; + + /* is it a file or directory? */ + if (lstat(path, &st) < 0) { + return -1; + } + + /* a file, so unlink it */ + if (!S_ISDIR(st.st_mode)) { + return unlink(path); + } + + /* a directory, so open handle */ + dir = opendir(path); + if (dir == NULL) { + return -1; + } + + /* recurse over components */ + errno = 0; + while ((de = readdir(dir)) != NULL) { +//TODO: don't blow the stack + char dn[PATH_MAX]; + if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { + continue; + } + snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); + if (dirUnlinkHierarchy(dn) < 0) { + fail = 1; + break; + } + errno = 0; + } + /* in case readdir or unlink_recursive failed */ + if (fail || errno < 0) { + int save = errno; + closedir(dir); + errno = save; + return -1; + } + + /* close directory handle */ + if (closedir(dir) < 0) { + return -1; + } + + /* delete target directory */ + return rmdir(path); +} + +int +dirSetHierarchyPermissions(const char *path, + int uid, int gid, int dirMode, int fileMode) +{ + struct stat st; + if (lstat(path, &st)) { + return -1; + } + + /* ignore symlinks */ + if (S_ISLNK(st.st_mode)) { + return 0; + } + + /* directories and files get different permissions */ + if (chown(path, uid, gid) || + chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) { + return -1; + } + + /* recurse over directory components */ + if (S_ISDIR(st.st_mode)) { + DIR *dir = opendir(path); + if (dir == NULL) { + return -1; + } + + errno = 0; + const struct dirent *de; + while (errno == 0 && (de = readdir(dir)) != NULL) { + if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { + continue; + } + + char dn[PATH_MAX]; + snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); + if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) { + errno = 0; + } else if (errno == 0) { + errno = -1; + } + } + + if (errno != 0) { + int save = errno; + closedir(dir); + errno = save; + return -1; + } + + if (closedir(dir)) { + return -1; + } + } + + return 0; +} |