otautil: Clean up dirCreateHierarchy().
- Changed to std::string based implementation (mostly moved from the former make_parents() in updater/install.cpp); - Removed the timestamp parameter, which is only neeed by file-based OTA; - Changed the type of mode from int to mode_t; - Renamed dirCreateHierarchy() to mkdir_recursively(). Test: recovery_unit_test passes. Test: No external user of dirCreateHierarchy() in code search. Change-Id: I71f8c4b29bab625513bbc3af6d0d1ecdc3a2719a
This commit is contained in:
parent
231c627a55
commit
ac3d1edca0
4 changed files with 124 additions and 182 deletions
|
@ -16,147 +16,101 @@
|
||||||
|
|
||||||
#include "DirUtil.h"
|
#include "DirUtil.h"
|
||||||
|
|
||||||
#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 <dirent.h>
|
||||||
#include <limits.h>
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <selinux/label.h>
|
#include <selinux/label.h>
|
||||||
#include <selinux/selinux.h>
|
#include <selinux/selinux.h>
|
||||||
|
|
||||||
typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
|
enum class DirStatus { DMISSING, DDIR, DILLEGAL };
|
||||||
|
|
||||||
static DirStatus
|
static DirStatus dir_status(const std::string& path) {
|
||||||
getPathDirStatus(const char *path)
|
struct stat sb;
|
||||||
{
|
if (stat(path.c_str(), &sb) == 0) {
|
||||||
struct stat st;
|
// Something's there; make sure it's a directory.
|
||||||
int err;
|
if (S_ISDIR(sb.st_mode)) {
|
||||||
|
return DirStatus::DDIR;
|
||||||
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;
|
errno = ENOTDIR;
|
||||||
|
return DirStatus::DILLEGAL;
|
||||||
|
} else if (errno != ENOENT) {
|
||||||
|
// Something went wrong, or something in the path is bad. Can't do anything in this situation.
|
||||||
|
return DirStatus::DILLEGAL;
|
||||||
|
}
|
||||||
|
return DirStatus::DMISSING;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int mkdir_recursively(const std::string& input_path, mode_t mode, bool strip_filename,
|
||||||
dirCreateHierarchy(const char *path, int mode,
|
const selabel_handle* sehnd) {
|
||||||
const struct utimbuf *timestamp, bool stripFileName,
|
// Check for an empty string before we bother making any syscalls.
|
||||||
struct selabel_handle *sehnd)
|
if (input_path.empty()) {
|
||||||
{
|
errno = ENOENT;
|
||||||
DirStatus ds;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for an empty string before we bother
|
// Allocate a path that we can modify; stick a slash on the end to make things easier.
|
||||||
* making any syscalls.
|
std::string path = input_path;
|
||||||
*/
|
if (strip_filename) {
|
||||||
if (path[0] == '\0') {
|
// Strip everything after the last slash.
|
||||||
errno = ENOENT;
|
size_t pos = path.rfind('/');
|
||||||
return -1;
|
if (pos == std::string::npos) {
|
||||||
}
|
errno = ENOENT;
|
||||||
// Allocate a path that we can modify; stick a slash on
|
return -1;
|
||||||
// the end to make things easier.
|
|
||||||
std::string cpath = path;
|
|
||||||
if (stripFileName) {
|
|
||||||
// Strip everything after the last slash.
|
|
||||||
size_t pos = cpath.rfind('/');
|
|
||||||
if (pos == std::string::npos) {
|
|
||||||
errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
cpath.resize(pos + 1);
|
|
||||||
} else {
|
|
||||||
// Make sure that the path ends in a slash.
|
|
||||||
cpath.push_back('/');
|
|
||||||
}
|
}
|
||||||
|
path.resize(pos + 1);
|
||||||
|
} else {
|
||||||
|
// Make sure that the path ends in a slash.
|
||||||
|
path.push_back('/');
|
||||||
|
}
|
||||||
|
|
||||||
/* See if it already exists.
|
// See if it already exists.
|
||||||
*/
|
DirStatus ds = dir_status(path);
|
||||||
ds = getPathDirStatus(cpath.c_str());
|
if (ds == DirStatus::DDIR) {
|
||||||
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.
|
|
||||||
*/
|
|
||||||
const char *path_start = &cpath[0];
|
|
||||||
char *p = &cpath[0];
|
|
||||||
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(path_start);
|
|
||||||
if (ds == DILLEGAL) {
|
|
||||||
/* Could happen if some other process/thread is
|
|
||||||
* messing with the filesystem.
|
|
||||||
*/
|
|
||||||
return -1;
|
|
||||||
} else if (ds == DMISSING) {
|
|
||||||
int err;
|
|
||||||
|
|
||||||
char *secontext = NULL;
|
|
||||||
|
|
||||||
if (sehnd) {
|
|
||||||
selabel_lookup(sehnd, &secontext, path_start, mode);
|
|
||||||
setfscreatecon(secontext);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = mkdir(path_start, mode);
|
|
||||||
|
|
||||||
if (secontext) {
|
|
||||||
freecon(secontext);
|
|
||||||
setfscreatecon(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (timestamp != NULL && utime(path_start, timestamp)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// else, this directory already exists.
|
|
||||||
|
|
||||||
// Repair the path and continue.
|
|
||||||
*p = '/';
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (ds == DirStatus::DILLEGAL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk up the path from the root and make each level.
|
||||||
|
size_t prev_end = 0;
|
||||||
|
while (prev_end < path.size()) {
|
||||||
|
size_t next_end = path.find('/', prev_end + 1);
|
||||||
|
if (next_end == std::string::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::string dir_path = path.substr(0, next_end);
|
||||||
|
// Check this part of the path and make a new directory if necessary.
|
||||||
|
switch (dir_status(dir_path)) {
|
||||||
|
case DirStatus::DILLEGAL:
|
||||||
|
// Could happen if some other process/thread is messing with the filesystem.
|
||||||
|
return -1;
|
||||||
|
case DirStatus::DMISSING: {
|
||||||
|
char* secontext = nullptr;
|
||||||
|
if (sehnd) {
|
||||||
|
selabel_lookup(const_cast<selabel_handle*>(sehnd), &secontext, dir_path.c_str(), mode);
|
||||||
|
setfscreatecon(secontext);
|
||||||
|
}
|
||||||
|
int err = mkdir(dir_path.c_str(), mode);
|
||||||
|
if (secontext) {
|
||||||
|
freecon(secontext);
|
||||||
|
setfscreatecon(nullptr);
|
||||||
|
}
|
||||||
|
if (err != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Already exists.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev_end = next_end;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,28 +14,26 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MINZIP_DIRUTIL_H_
|
#ifndef OTAUTIL_DIRUTIL_H_
|
||||||
#define MINZIP_DIRUTIL_H_
|
#define OTAUTIL_DIRUTIL_H_
|
||||||
|
|
||||||
#include <utime.h>
|
#include <sys/stat.h> // mode_t
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct selabel_handle;
|
struct selabel_handle;
|
||||||
|
|
||||||
/* Like "mkdir -p", try to guarantee that all directories
|
// Like "mkdir -p", try to guarantee that all directories specified in path are present, creating as
|
||||||
* specified in path are present, creating as many directories
|
// many directories as necessary. The specified mode is passed to all mkdir calls; no modifications
|
||||||
* as necessary. The specified mode is passed to all mkdir
|
// are made to umask.
|
||||||
* calls; no modifications are made to umask.
|
//
|
||||||
*
|
// If strip_filename is set, everything after the final '/' is stripped before creating the
|
||||||
* If stripFileName is set, everything after the final '/'
|
// directory
|
||||||
* is stripped before creating the directory hierarchy.
|
// hierarchy.
|
||||||
*
|
//
|
||||||
* If timestamp is non-NULL, new directories will be timestamped accordingly.
|
// Returns 0 on success; returns -1 (and sets errno) on failure (usually if some element of path is
|
||||||
*
|
// not a directory).
|
||||||
* Returns 0 on success; returns -1 (and sets errno) on failure
|
int mkdir_recursively(const std::string& path, mode_t mode, bool strip_filename,
|
||||||
* (usually if some element of path is not a directory).
|
const struct selabel_handle* sehnd);
|
||||||
*/
|
|
||||||
int dirCreateHierarchy(const char *path, int mode,
|
|
||||||
const struct utimbuf *timestamp, bool stripFileName,
|
|
||||||
struct selabel_handle* sehnd);
|
|
||||||
|
|
||||||
#endif // MINZIP_DIRUTIL_H_
|
#endif // OTAUTIL_DIRUTIL_H_
|
||||||
|
|
26
recovery.cpp
26
recovery.cpp
|
@ -178,19 +178,19 @@ struct selabel_handle* sehandle;
|
||||||
* 7b. the user reboots (pulling the battery, etc) into the main system
|
* 7b. the user reboots (pulling the battery, etc) into the main system
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// open a given path, mounting partitions as necessary
|
// Open a given path, mounting partitions as necessary.
|
||||||
FILE* fopen_path(const char *path, const char *mode) {
|
FILE* fopen_path(const char* path, const char* mode) {
|
||||||
if (ensure_path_mounted(path) != 0) {
|
if (ensure_path_mounted(path) != 0) {
|
||||||
LOG(ERROR) << "Can't mount " << path;
|
LOG(ERROR) << "Can't mount " << path;
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When writing, try to create the containing directory, if necessary.
|
// When writing, try to create the containing directory, if necessary. Use generous permissions,
|
||||||
// Use generous permissions, the system (init.rc) will reset them.
|
// the system (init.rc) will reset them.
|
||||||
if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1, sehandle);
|
if (strchr("wa", mode[0])) {
|
||||||
|
mkdir_recursively(path, 0777, true, sehandle);
|
||||||
FILE *fp = fopen(path, mode);
|
}
|
||||||
return fp;
|
return fopen(path, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// close a file, log an error if the error indicator is set
|
// close a file, log an error if the error indicator is set
|
||||||
|
@ -593,7 +593,7 @@ static bool erase_volume(const char* volume) {
|
||||||
if (is_cache) {
|
if (is_cache) {
|
||||||
// Re-create the log dir and write back the log entries.
|
// Re-create the log dir and write back the log entries.
|
||||||
if (ensure_path_mounted(CACHE_LOG_DIR) == 0 &&
|
if (ensure_path_mounted(CACHE_LOG_DIR) == 0 &&
|
||||||
dirCreateHierarchy(CACHE_LOG_DIR, 0777, nullptr, false, sehandle) == 0) {
|
mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle) == 0) {
|
||||||
for (const auto& log : log_files) {
|
for (const auto& log : log_files) {
|
||||||
if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
|
if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
|
||||||
log.sb.st_gid)) {
|
log.sb.st_gid)) {
|
||||||
|
|
|
@ -26,23 +26,23 @@
|
||||||
|
|
||||||
TEST(DirUtilTest, create_invalid) {
|
TEST(DirUtilTest, create_invalid) {
|
||||||
// Requesting to create an empty dir is invalid.
|
// Requesting to create an empty dir is invalid.
|
||||||
ASSERT_EQ(-1, dirCreateHierarchy("", 0755, nullptr, false, nullptr));
|
ASSERT_EQ(-1, mkdir_recursively("", 0755, false, nullptr));
|
||||||
ASSERT_EQ(ENOENT, errno);
|
ASSERT_EQ(ENOENT, errno);
|
||||||
|
|
||||||
// Requesting to strip the name with no slash present.
|
// Requesting to strip the name with no slash present.
|
||||||
ASSERT_EQ(-1, dirCreateHierarchy("abc", 0755, nullptr, true, nullptr));
|
ASSERT_EQ(-1, mkdir_recursively("abc", 0755, true, nullptr));
|
||||||
ASSERT_EQ(ENOENT, errno);
|
ASSERT_EQ(ENOENT, errno);
|
||||||
|
|
||||||
// Creating a dir that already exists.
|
// Creating a dir that already exists.
|
||||||
TemporaryDir td;
|
TemporaryDir td;
|
||||||
ASSERT_EQ(0, dirCreateHierarchy(td.path, 0755, nullptr, false, nullptr));
|
ASSERT_EQ(0, mkdir_recursively(td.path, 0755, false, nullptr));
|
||||||
|
|
||||||
// "///" is a valid dir.
|
// "///" is a valid dir.
|
||||||
ASSERT_EQ(0, dirCreateHierarchy("///", 0755, nullptr, false, nullptr));
|
ASSERT_EQ(0, mkdir_recursively("///", 0755, false, nullptr));
|
||||||
|
|
||||||
// Request to create a dir, but a file with the same name already exists.
|
// Request to create a dir, but a file with the same name already exists.
|
||||||
TemporaryFile tf;
|
TemporaryFile tf;
|
||||||
ASSERT_EQ(-1, dirCreateHierarchy(tf.path, 0755, nullptr, false, nullptr));
|
ASSERT_EQ(-1, mkdir_recursively(tf.path, 0755, false, nullptr));
|
||||||
ASSERT_EQ(ENOTDIR, errno);
|
ASSERT_EQ(ENOTDIR, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ TEST(DirUtilTest, create_smoke) {
|
||||||
std::string prefix(td.path);
|
std::string prefix(td.path);
|
||||||
std::string path = prefix + "/a/b";
|
std::string path = prefix + "/a/b";
|
||||||
constexpr mode_t mode = 0755;
|
constexpr mode_t mode = 0755;
|
||||||
ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), mode, nullptr, false, nullptr));
|
ASSERT_EQ(0, mkdir_recursively(path, mode, false, nullptr));
|
||||||
|
|
||||||
// Verify.
|
// Verify.
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
|
@ -69,7 +69,7 @@ TEST(DirUtilTest, create_strip_filename) {
|
||||||
TemporaryDir td;
|
TemporaryDir td;
|
||||||
std::string prefix(td.path);
|
std::string prefix(td.path);
|
||||||
std::string path = prefix + "/a/b";
|
std::string path = prefix + "/a/b";
|
||||||
ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), 0755, nullptr, true, nullptr));
|
ASSERT_EQ(0, mkdir_recursively(path, 0755, true, nullptr));
|
||||||
|
|
||||||
// Verify that "../a" exists but not "../a/b".
|
// Verify that "../a" exists but not "../a/b".
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
|
@ -83,31 +83,21 @@ TEST(DirUtilTest, create_strip_filename) {
|
||||||
ASSERT_EQ(0, rmdir((prefix + "/a").c_str()));
|
ASSERT_EQ(0, rmdir((prefix + "/a").c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DirUtilTest, create_mode_and_timestamp) {
|
TEST(DirUtilTest, create_mode) {
|
||||||
TemporaryDir td;
|
TemporaryDir td;
|
||||||
std::string prefix(td.path);
|
std::string prefix(td.path);
|
||||||
std::string path = prefix + "/a/b";
|
std::string path = prefix + "/a/b";
|
||||||
// Set the timestamp to 8/1/2008.
|
|
||||||
constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };
|
|
||||||
constexpr mode_t mode = 0751;
|
constexpr mode_t mode = 0751;
|
||||||
ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), mode, ×tamp, false, nullptr));
|
ASSERT_EQ(0, mkdir_recursively(path, mode, false, nullptr));
|
||||||
|
|
||||||
// Verify the mode and timestamp for "../a/b".
|
// Verify the mode for "../a/b".
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
ASSERT_EQ(0, stat(path.c_str(), &sb)) << strerror(errno);
|
ASSERT_EQ(0, stat(path.c_str(), &sb)) << strerror(errno);
|
||||||
ASSERT_TRUE(S_ISDIR(sb.st_mode));
|
ASSERT_TRUE(S_ISDIR(sb.st_mode));
|
||||||
constexpr mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
|
constexpr mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
|
||||||
ASSERT_EQ(mode, sb.st_mode & mask);
|
ASSERT_EQ(mode, sb.st_mode & mask);
|
||||||
|
|
||||||
timespec time;
|
// Verify the mode for "../a".
|
||||||
time.tv_sec = 1217592000;
|
|
||||||
time.tv_nsec = 0;
|
|
||||||
|
|
||||||
ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
|
|
||||||
ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
|
|
||||||
|
|
||||||
// Verify the mode for "../a". Note that the timestamp for intermediate directories (e.g. "../a")
|
|
||||||
// may not be 'timestamp' according to the current implementation.
|
|
||||||
ASSERT_EQ(0, stat((prefix + "/a").c_str(), &sb)) << strerror(errno);
|
ASSERT_EQ(0, stat((prefix + "/a").c_str(), &sb)) << strerror(errno);
|
||||||
ASSERT_TRUE(S_ISDIR(sb.st_mode));
|
ASSERT_TRUE(S_ISDIR(sb.st_mode));
|
||||||
ASSERT_EQ(mode, sb.st_mode & mask);
|
ASSERT_EQ(mode, sb.st_mode & mask);
|
||||||
|
|
Loading…
Reference in a new issue