[ Merge of http://go/wvgerrit/120763 ] This change introduces additional logging information for files and file system operations on Android. File reading and writing will attempt to make sense of |errno| and log useful information. In the event that the file must be closed, the file stat will be printed. Failures in determining the file size will print potential reasons for the encountered error. This partly restructures the File interface implementation to use file descriptors instead of the C standard libraries FILE handle. This is done to ensure that |errno| is set to an expected value. This change also introduces the utility functions SafeWrite() and SafeRead() to handle common, retriable errors. Bug: 178232354 Test: Android MediaDrm GTS and Android file-based unittests Change-Id: I15a3c47a271098c9edb4bd9f619ed1a12dca6143
232 lines
6.3 KiB
C++
232 lines
6.3 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine License
|
|
// Agreement.
|
|
|
|
#include "file_utils.h"
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <sys/sendfile.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cstring>
|
|
|
|
#include "file_store.h"
|
|
#include "log.h"
|
|
#include "properties.h"
|
|
|
|
namespace wvcdm {
|
|
|
|
bool IsCurrentOrParentDirectory(const char* dir) {
|
|
return strcmp(dir, kCurrentDirectory) == 0 ||
|
|
strcmp(dir, kParentDirectory) == 0;
|
|
}
|
|
|
|
bool FileUtils::Exists(const std::string& path) {
|
|
struct stat buf;
|
|
int res = stat(path.c_str(), &buf) == 0;
|
|
if (!res) {
|
|
LOGV("File::Exists: stat failed: %d, %s", errno, strerror(errno));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool FileUtils::Remove(const std::string& path) {
|
|
if (FileUtils::IsDirectory(path)) {
|
|
// Handle directory deletion
|
|
DIR* dir;
|
|
if ((dir = opendir(path.c_str())) != nullptr) {
|
|
// first remove files and dir within it
|
|
struct dirent* entry;
|
|
while ((entry = readdir(dir)) != nullptr) {
|
|
if (!IsCurrentOrParentDirectory(entry->d_name)) {
|
|
std::string path_to_remove = path + kDirectoryDelimiter;
|
|
path_to_remove += entry->d_name;
|
|
if (!Remove(path_to_remove)) {
|
|
closedir(dir);
|
|
LOGW("Failed to remove directory entry: dir_path = %s, entry = %s",
|
|
path.c_str(), entry->d_name);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
if (rmdir(path.c_str())) {
|
|
LOGW("File::Remove: rmdir failed: %d, %s", errno, strerror(errno));
|
|
return false;
|
|
}
|
|
return true;
|
|
} else {
|
|
size_t wildcard_pos = path.find(kWildcard);
|
|
if (wildcard_pos == std::string::npos) {
|
|
// Handle file deletion
|
|
if (unlink(path.c_str()) && (errno != ENOENT)) {
|
|
LOGW("File::Remove: unlink failed: %d, %s", errno, strerror(errno));
|
|
return false;
|
|
}
|
|
} else {
|
|
// Handle wildcard specified file deletion
|
|
size_t delimiter_pos = path.rfind(kDirectoryDelimiter, wildcard_pos);
|
|
if (delimiter_pos == std::string::npos) {
|
|
LOGW("File::Remove: unable to find path delimiter before wildcard");
|
|
return false;
|
|
}
|
|
|
|
DIR* dir;
|
|
std::string dir_path = path.substr(0, delimiter_pos);
|
|
std::string prepend =
|
|
path.substr(delimiter_pos + 1, wildcard_pos - delimiter_pos - 1);
|
|
if ((dir = opendir(dir_path.c_str())) == nullptr) {
|
|
LOGW("File::Remove: directory open failed for wildcard: %d, %s", errno,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
struct dirent* entry;
|
|
std::string ext = path.substr(wildcard_pos + 1);
|
|
|
|
while ((entry = readdir(dir)) != nullptr) {
|
|
size_t filename_len = strlen(entry->d_name);
|
|
if (filename_len > ext.size()) {
|
|
if (strcmp(entry->d_name + filename_len - ext.size(), ext.c_str()) ==
|
|
0 &&
|
|
!IsCurrentOrParentDirectory(entry->d_name) &&
|
|
strncmp(entry->d_name, prepend.c_str(), prepend.size()) == 0) {
|
|
std::string file_path_to_remove =
|
|
dir_path + kDirectoryDelimiter + entry->d_name;
|
|
if (!Remove(file_path_to_remove)) {
|
|
closedir(dir);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool FileUtils::Copy(const std::string& src, const std::string& dest) {
|
|
struct stat stat_buf;
|
|
if (stat(src.c_str(), &stat_buf)) {
|
|
LOGV("File::Copy: file %s stat error: %d, %s", src.c_str(), errno,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
int fd_src = open(src.c_str(), O_RDONLY);
|
|
if (fd_src < 0) {
|
|
LOGW("File::Copy: unable to open file %s: %d, %s", src.c_str(), errno,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
int fd_dest = open(dest.c_str(), O_WRONLY | O_CREAT, stat_buf.st_mode);
|
|
if (fd_dest < 0) {
|
|
LOGW("File::Copy: unable to open file %s: %d, %s", dest.c_str(), errno,
|
|
strerror(errno));
|
|
close(fd_src);
|
|
return false;
|
|
}
|
|
|
|
off_t offset = 0;
|
|
bool status = true;
|
|
if (sendfile(fd_dest, fd_src, &offset, stat_buf.st_size) < 0) {
|
|
LOGV("File::Copy: unable to copy %s to %s: %d, %s", src.c_str(),
|
|
dest.c_str(), errno, strerror(errno));
|
|
status = false;
|
|
}
|
|
|
|
close(fd_src);
|
|
close(fd_dest);
|
|
return status;
|
|
}
|
|
|
|
bool FileUtils::List(const std::string& path, std::vector<std::string>* files) {
|
|
if (nullptr == files) {
|
|
LOGV("File::List: files destination not provided");
|
|
return false;
|
|
}
|
|
|
|
if (!FileUtils::Exists(path)) {
|
|
LOGV("File::List: path %s does not exist: %d, %s", path.c_str(), errno,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
DIR* dir = opendir(path.c_str());
|
|
if (dir == nullptr) {
|
|
LOGW("File::List: unable to open directory %s: %d, %s", path.c_str(), errno,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
files->clear();
|
|
struct dirent* entry;
|
|
while ((entry = readdir(dir)) != nullptr) {
|
|
if (!IsCurrentOrParentDirectory(entry->d_name)) {
|
|
files->push_back(entry->d_name);
|
|
}
|
|
}
|
|
closedir(dir);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FileUtils::IsRegularFile(const std::string& path) {
|
|
struct stat buf;
|
|
if (stat(path.c_str(), &buf) == 0)
|
|
return buf.st_mode & S_IFREG;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool FileUtils::IsDirectory(const std::string& path) {
|
|
struct stat buf;
|
|
if (stat(path.c_str(), &buf) == 0)
|
|
return buf.st_mode & S_IFDIR;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool FileUtils::CreateDirectory(const std::string& path_in) {
|
|
std::string path = path_in;
|
|
size_t size = path.size();
|
|
if ((size == 1) && (path[0] == kDirectoryDelimiter)) return true;
|
|
|
|
if (size <= 1) return false;
|
|
|
|
size_t pos = path.find(kDirectoryDelimiter, 1);
|
|
while (pos < size) {
|
|
path[pos] = '\0';
|
|
if (mkdir(path.c_str(), 0700) != 0) {
|
|
if (errno != EEXIST) {
|
|
LOGW("File::CreateDirectory: mkdir failed: %d, %s\n", errno,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
}
|
|
path[pos] = kDirectoryDelimiter;
|
|
pos = path.find(kDirectoryDelimiter, pos + 1);
|
|
}
|
|
|
|
if (path[size - 1] != kDirectoryDelimiter) {
|
|
if (mkdir(path.c_str(), 0700) != 0) {
|
|
if (errno != EEXIST) {
|
|
LOGW("File::CreateDirectory: mkdir failed: %d, %s\n", errno,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace wvcdm
|