blob: e6c605d2c2e3e56595085d4d0263b012600a7bdb [file] [log] [blame]
/*
* Copyright (C) 2020 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 "tools/base/deploy/common/io.h"
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include "tools/base/deploy/common/env.h"
#include "tools/base/deploy/common/event.h"
#include "tools/base/deploy/common/log.h"
#include "tools/base/deploy/common/utils.h"
namespace deploy {
std::string IO::ResolvePath(const std::string& path) {
// Only apply the prefix to absolute paths.
if (!Env::root().empty() && path.size() > 0 && path[0] == '/') {
return Env::root() + path;
}
return path;
}
int IO::access(const std::string& pathname, int mode) {
return ::access(ResolvePath(pathname).c_str(), mode);
}
int IO::creat(const std::string& pathname, mode_t mode) {
return ::creat(ResolvePath(pathname).c_str(), mode);
}
FILE* IO::fopen(const std::string& filename, const std::string& mode) {
return ::fopen(ResolvePath(filename).c_str(), mode.c_str());
}
int IO::stat(const std::string& pathname, struct stat* statbuf) {
int ret = ::stat(ResolvePath(pathname).c_str(), statbuf);
#ifdef __ANDROID__
return ret;
#else
// In tests contexts, we need to do some trickery re: /proc entries, because
// we use Android-specific UID conventions in Apply Changes.
if (ret != 0 || pathname.find("/proc") != 0) {
return ret;
}
FILE* uid = IO::fopen(pathname + "/.uid", "r");
if (uid == nullptr) {
Log::E("Cannot fake-stat %s", pathname.c_str());
return 1;
}
fscanf(uid, "%d", &statbuf->st_uid);
fclose(uid);
return 0;
#endif
}
int IO::chmod(const std::string& pathname, mode_t mode) {
return ::chmod(ResolvePath(pathname).c_str(), mode);
}
int IO::mkdir(const std::string& pathname, mode_t mode) {
return ::mkdir(ResolvePath(pathname).c_str(), mode);
}
static bool DirectoryExists(const char* dir_path) {
struct stat sb;
return (stat(dir_path, &sb) == 0 && S_ISDIR(sb.st_mode));
}
bool IO::mkpath(const std::string& p, mode_t mode) {
std::string resolvedPath = ResolvePath(p);
char path[resolvedPath.size() + 1];
strcpy(path, resolvedPath.c_str());
if (path[resolvedPath.size() - 1] == '/') {
path[resolvedPath.size() - 1] = 0;
}
if (DirectoryExists(path)) {
return true;
}
size_t path_size = strlen(path);
for (size_t i = 0; i < path_size; i++) {
if (path[i] != '/' || i == 0) {
continue;
}
char c = path[i]; // Save character for patching/restoring
path[i] = '\0'; // Patch / with string termination.
if (!DirectoryExists(path)) {
int error = ::mkdir(path, mode);
if (error) {
std::string err_msg = "Unable to create '"_s + path + "'";
err_msg += " reason:'"_s + strerror(errno) + "'";
ErrEvent(err_msg);
return false;
}
}
path[i] = c;
}
// The last directory was not created in the loop.
int error = ::mkdir(path, mode);
if (error) {
std::string err_msg = "Unable to create '"_s + path + "'";
err_msg += " reason:'"_s + strerror(errno) + "'";
ErrEvent(err_msg);
}
if (!DirectoryExists(path)) {
std::string err_msg = "Unable to create '"_s + path + "'";
err_msg += " reason:'"_s + strerror(errno) + "'";
ErrEvent(err_msg);
return false;
}
return true;
}
int IO::open(const std::string& pathname, int flags) {
return ::open(ResolvePath(pathname).c_str(), flags);
}
int IO::open(const std::string& pathname, int flags, mode_t mode) {
return ::open(ResolvePath(pathname).c_str(), flags, mode);
}
DIR* IO::opendir(const std::string& name) {
return ::opendir(ResolvePath(name).c_str());
}
int IO::unlink(const std::string& pathname) {
return ::unlink(ResolvePath(pathname).c_str());
}
int IO::rmdir(const std::string& pathname) {
std::string path = ResolvePath(pathname);
DIR* dir = ::opendir(path.c_str());
if (dir == nullptr) {
return 1;
}
dirent* ent;
while ((ent = readdir(dir)) != nullptr) {
if (ent->d_type == DT_REG) {
std::string full_path = path + "/" + ent->d_name;
int status = ::unlink(full_path.c_str());
if (status) {
closedir(dir);
return status;
}
}
// No use case for recursive rmdir yet.
}
closedir(dir);
return ::rmdir(path.c_str());
}
} // namespace deploy