Files
android/libwvdrmengine/cdm/util/src/file_utils.cpp
Alex Dale 36dc9bb97b Extended Android file store error logs.
[ 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
2021-04-09 14:23:24 -07:00

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