Resolve android child Content-URIs by display-name
In GetFile(), GetDirectory() and Remove(), we must resolve the child path by calling to ContentUriGetChildDocumentOrQuery(). If the path already exists, we can use it as the backing path of the handle. If the path does not exist, it is not possible to create the document until all access checks have been done. So to handle this, a query URI is created which encodes the relevant information. This URI will eventually be used by NativeFileUtil::EnsureFileExists() or CreateDirectory() to call ContentUriGetDocumentFromQuery() to create the document. In DidGetFile() and DidGetDirectory() we check via ContentUriIsCreateChildDocumentQuery() to detect if a query URI is being used and we update the handle with the valid URI of the newly created document. Bug: 376097631 Change-Id: I5e4b22f6668e49bd1ddb760a044fb17fd0f02a7e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5981535 Reviewed-by: Nathan Memmott <memmott@chromium.org> Commit-Queue: Joel Hockey <joelhockey@chromium.org> Cr-Commit-Position: refs/heads/main@{#1379263}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
463c2f330b
commit
531424b7e3
content/browser/file_system_access
file_system_access_directory_handle_impl.ccfile_system_access_directory_handle_impl.hfile_system_access_file_handle_impl_unittest.cc
storage/browser/file_system
@ -14,7 +14,9 @@
|
||||
#include "base/memory/ref_counted_delete_on_sequence.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/bind_post_task.h"
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
#include "base/task/task_traits.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/uuid.h"
|
||||
#include "build/build_config.h"
|
||||
@ -37,6 +39,7 @@
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
#include "base/android/content_uri_utils.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#endif
|
||||
|
||||
using blink::mojom::FileSystemAccessEntry;
|
||||
@ -130,6 +133,32 @@ void FileSystemAccessDirectoryHandleImpl::GetFile(const std::string& basename,
|
||||
GetFileCallback callback) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
// Lookup content-URI by display-name. If the file does not exist, and
|
||||
// `create` is set, ContentUriGetChildDocumentOrQuery() will return a
|
||||
// create-child-document query URI which is used in
|
||||
// NativeFileUtil::EnsureFileExists() to call ContentUriGetDocumentFromQuery()
|
||||
// and create the document. DidGetFile() will then update the child path
|
||||
// before creating the returned handle.
|
||||
if (url().virtual_path().IsContentUri()) {
|
||||
std::string mime_type;
|
||||
std::string ext = base::FilePath(basename).Extension();
|
||||
if (ext.empty() ||
|
||||
!net::GetWellKnownMimeTypeFromExtension(ext.substr(1), &mime_type)) {
|
||||
mime_type = "application/octet-stream";
|
||||
}
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
|
||||
base::BindOnce(&base::ContentUriGetChildDocumentOrQuery,
|
||||
url().virtual_path(), basename, mime_type,
|
||||
/*is_directory=*/false, create),
|
||||
base::BindOnce(
|
||||
&FileSystemAccessDirectoryHandleImpl::OnGetFileContentUri,
|
||||
weak_factory_.GetWeakPtr(), basename, create, std::move(callback)));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
storage::FileSystemURL child_url;
|
||||
blink::mojom::FileSystemAccessErrorPtr get_child_url_result =
|
||||
GetChildURL(basename, &child_url);
|
||||
@ -137,6 +166,25 @@ void FileSystemAccessDirectoryHandleImpl::GetFile(const std::string& basename,
|
||||
std::move(get_child_url_result), std::move(child_url));
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
void FileSystemAccessDirectoryHandleImpl::OnGetFileContentUri(
|
||||
std::string basename,
|
||||
bool create,
|
||||
GetFileCallback callback,
|
||||
base::FilePath child_path) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (child_path.empty()) {
|
||||
std::move(callback).Run(file_system_access_error::FromFileError(
|
||||
base::File::FILE_ERROR_NOT_FOUND),
|
||||
mojo::NullRemote());
|
||||
return;
|
||||
}
|
||||
|
||||
GetFileResolved(basename, create, std::move(callback),
|
||||
file_system_access_error::Ok(), CreateChildURL(child_path));
|
||||
}
|
||||
#endif
|
||||
|
||||
void FileSystemAccessDirectoryHandleImpl::GetFileResolved(
|
||||
const std::string& basename,
|
||||
bool create,
|
||||
@ -224,6 +272,27 @@ void FileSystemAccessDirectoryHandleImpl::GetDirectory(
|
||||
GetDirectoryCallback callback) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
// Lookup content-URI by display-name. If the directory does not exist, and
|
||||
// `create` is set, ContentUriGetChildDocumentOrQuery() will return a
|
||||
// create-child-document query URI which is used in
|
||||
// NativeFileUtil::CreateDirectory() to call ContentUriGetDocumentFromQuery()
|
||||
// and create the document. DidGetDirectory() will then update the child path
|
||||
// before creating the returned handle.
|
||||
if (url().virtual_path().IsContentUri()) {
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
|
||||
base::BindOnce(&base::ContentUriGetChildDocumentOrQuery,
|
||||
url().virtual_path(), basename,
|
||||
/*mime_type=*/std::string(),
|
||||
/*is_directory=*/true, create),
|
||||
base::BindOnce(
|
||||
&FileSystemAccessDirectoryHandleImpl::OnGetDirectoryContentUri,
|
||||
weak_factory_.GetWeakPtr(), basename, create, std::move(callback)));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
storage::FileSystemURL child_url;
|
||||
blink::mojom::FileSystemAccessErrorPtr get_child_url_result =
|
||||
GetChildURL(basename, &child_url);
|
||||
@ -231,6 +300,26 @@ void FileSystemAccessDirectoryHandleImpl::GetDirectory(
|
||||
std::move(get_child_url_result), std::move(child_url));
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
void FileSystemAccessDirectoryHandleImpl::OnGetDirectoryContentUri(
|
||||
std::string basename,
|
||||
bool create,
|
||||
GetDirectoryCallback callback,
|
||||
base::FilePath child_path) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (child_path.empty()) {
|
||||
std::move(callback).Run(file_system_access_error::FromFileError(
|
||||
base::File::FILE_ERROR_NOT_FOUND),
|
||||
mojo::NullRemote());
|
||||
return;
|
||||
}
|
||||
|
||||
GetDirectoryResolved(basename, create, std::move(callback),
|
||||
file_system_access_error::Ok(),
|
||||
CreateChildURL(child_path));
|
||||
}
|
||||
#endif
|
||||
|
||||
void FileSystemAccessDirectoryHandleImpl::GetDirectoryResolved(
|
||||
const std::string& basename,
|
||||
bool create,
|
||||
@ -342,6 +431,23 @@ void FileSystemAccessDirectoryHandleImpl::RemoveEntry(
|
||||
RemoveEntryCallback callback) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
// Lookup content-URI by display-name.
|
||||
if (url().virtual_path().IsContentUri()) {
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
|
||||
base::BindOnce(&base::ContentUriGetChildDocumentOrQuery,
|
||||
url().virtual_path(), basename,
|
||||
/*mime_type=*/std::string(),
|
||||
/*is_directory=*/false, /*create=*/false),
|
||||
base::BindOnce(
|
||||
&FileSystemAccessDirectoryHandleImpl::OnRemoveEntryContentUri,
|
||||
weak_factory_.GetWeakPtr(), basename, recurse,
|
||||
std::move(callback)));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
storage::FileSystemURL child_url;
|
||||
blink::mojom::FileSystemAccessErrorPtr get_child_url_result =
|
||||
GetChildURL(basename, &child_url);
|
||||
@ -349,6 +455,25 @@ void FileSystemAccessDirectoryHandleImpl::RemoveEntry(
|
||||
std::move(get_child_url_result), std::move(child_url));
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
void FileSystemAccessDirectoryHandleImpl::OnRemoveEntryContentUri(
|
||||
std::string basename,
|
||||
bool recurse,
|
||||
RemoveEntryCallback callback,
|
||||
base::FilePath child_path) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (child_path.empty()) {
|
||||
std::move(callback).Run(file_system_access_error::FromFileError(
|
||||
base::File::FILE_ERROR_NOT_FOUND));
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveEntryResolved(basename, recurse, std::move(callback),
|
||||
file_system_access_error::Ok(),
|
||||
CreateChildURL(child_path));
|
||||
}
|
||||
#endif
|
||||
|
||||
void FileSystemAccessDirectoryHandleImpl::RemoveEntryResolved(
|
||||
const std::string& basename,
|
||||
bool recurse,
|
||||
@ -481,11 +606,29 @@ void FileSystemAccessDirectoryHandleImpl::GetFileWithWritePermission(
|
||||
}
|
||||
|
||||
void FileSystemAccessDirectoryHandleImpl::DidGetFile(
|
||||
const storage::FileSystemURL& url,
|
||||
storage::FileSystemURL child_url,
|
||||
GetFileCallback callback,
|
||||
base::File::Error result) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
// If getFile() was called with `create` for a non-existing file, then
|
||||
// `child_url` will be a create-child-document URI which would have been used
|
||||
// in NativeFileUtils::EnsureFileExists() to create the document, and we need
|
||||
// to lookup the document URI.
|
||||
base::FilePath child_path = child_url.path();
|
||||
if (result == base::File::FILE_OK &&
|
||||
base::ContentUriIsCreateChildDocumentQuery(child_path)) {
|
||||
child_path =
|
||||
base::ContentUriGetDocumentFromQuery(child_path, /*create=*/false);
|
||||
if (child_path.empty()) {
|
||||
result = base::File::FILE_ERROR_NOT_FOUND;
|
||||
} else {
|
||||
child_url = CreateChildURL(child_path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (result != base::File::FILE_OK) {
|
||||
std::move(callback).Run(file_system_access_error::FromFileError(result),
|
||||
mojo::NullRemote());
|
||||
@ -494,7 +637,7 @@ void FileSystemAccessDirectoryHandleImpl::DidGetFile(
|
||||
|
||||
std::move(callback).Run(
|
||||
file_system_access_error::Ok(),
|
||||
manager()->CreateFileHandle(context(), url, handle_state()));
|
||||
manager()->CreateFileHandle(context(), child_url, handle_state()));
|
||||
}
|
||||
|
||||
void FileSystemAccessDirectoryHandleImpl::GetDirectoryWithWritePermission(
|
||||
@ -514,11 +657,29 @@ void FileSystemAccessDirectoryHandleImpl::GetDirectoryWithWritePermission(
|
||||
}
|
||||
|
||||
void FileSystemAccessDirectoryHandleImpl::DidGetDirectory(
|
||||
const storage::FileSystemURL& url,
|
||||
storage::FileSystemURL child_url,
|
||||
GetDirectoryCallback callback,
|
||||
base::File::Error result) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
// If getDirectory() was called with `create` for a non-existing file, then
|
||||
// `child_url` will be a create-child-document URI which would have been used
|
||||
// in NativeFileUtils::CreateDirectory() to create the dir, and we need
|
||||
// to lookup the document URI.
|
||||
base::FilePath child_path = child_url.path();
|
||||
if (result == base::File::FILE_OK &&
|
||||
base::ContentUriIsCreateChildDocumentQuery(child_path)) {
|
||||
child_path =
|
||||
base::ContentUriGetDocumentFromQuery(child_path, /*create=*/false);
|
||||
if (child_path.empty()) {
|
||||
result = base::File::FILE_ERROR_NOT_FOUND;
|
||||
} else {
|
||||
child_url = CreateChildURL(child_path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (result != base::File::FILE_OK) {
|
||||
std::move(callback).Run(file_system_access_error::FromFileError(result),
|
||||
mojo::NullRemote());
|
||||
@ -527,7 +688,7 @@ void FileSystemAccessDirectoryHandleImpl::DidGetDirectory(
|
||||
|
||||
std::move(callback).Run(
|
||||
file_system_access_error::Ok(),
|
||||
manager()->CreateDirectoryHandle(context(), url, handle_state()));
|
||||
manager()->CreateDirectoryHandle(context(), child_url, handle_state()));
|
||||
}
|
||||
|
||||
void FileSystemAccessDirectoryHandleImpl::DidReadDirectory(
|
||||
@ -722,13 +883,21 @@ FileSystemAccessDirectoryHandleImpl::GetChildURL(
|
||||
base::FilePath child_path =
|
||||
parent.virtual_path().Append(base::FilePath::FromUTF8Unsafe(basename));
|
||||
#endif
|
||||
*result = file_system_context()->CreateCrackedFileSystemURL(
|
||||
parent.storage_key(), parent.mount_type(), child_path);
|
||||
*result = CreateChildURL(child_path);
|
||||
return file_system_access_error::Ok();
|
||||
}
|
||||
|
||||
storage::FileSystemURL FileSystemAccessDirectoryHandleImpl::CreateChildURL(
|
||||
const base::FilePath& child_path) {
|
||||
const storage::FileSystemURL& parent = url();
|
||||
storage::FileSystemURL child_url =
|
||||
file_system_context()->CreateCrackedFileSystemURL(
|
||||
parent.storage_key(), parent.mount_type(), child_path);
|
||||
// Child URLs inherit their parent's storage bucket.
|
||||
if (parent.bucket()) {
|
||||
result->SetBucket(parent.bucket().value());
|
||||
child_url.SetBucket(parent.bucket().value());
|
||||
}
|
||||
return file_system_access_error::Ok();
|
||||
return child_url;
|
||||
}
|
||||
|
||||
FileSystemAccessEntryPtr FileSystemAccessDirectoryHandleImpl::CreateEntry(
|
||||
|
@ -85,6 +85,12 @@ class CONTENT_EXPORT FileSystemAccessDirectoryHandleImpl
|
||||
storage::FileSystemURL* result);
|
||||
|
||||
private:
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
void OnGetFileContentUri(std::string basename,
|
||||
bool create,
|
||||
GetFileCallback callback,
|
||||
base::FilePath child_path);
|
||||
#endif
|
||||
void GetFileResolved(
|
||||
const std::string& basename,
|
||||
bool create,
|
||||
@ -96,13 +102,19 @@ class CONTENT_EXPORT FileSystemAccessDirectoryHandleImpl
|
||||
void GetFileWithWritePermission(const storage::FileSystemURL& child_url,
|
||||
GetFileCallback callback);
|
||||
void DoGetFile(bool create,
|
||||
storage::FileSystemURL url,
|
||||
storage::FileSystemURL child_url,
|
||||
GetFileCallback callback,
|
||||
FileSystemAccessPermissionContext::SensitiveEntryResult
|
||||
sensitive_entry_result);
|
||||
void DidGetFile(const storage::FileSystemURL& url,
|
||||
void DidGetFile(storage::FileSystemURL child_url,
|
||||
GetFileCallback callback,
|
||||
base::File::Error result);
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
void OnGetDirectoryContentUri(std::string basename,
|
||||
bool create,
|
||||
GetDirectoryCallback callback,
|
||||
base::FilePath child_path);
|
||||
#endif
|
||||
void GetDirectoryResolved(
|
||||
const std::string& basename,
|
||||
bool create,
|
||||
@ -113,7 +125,7 @@ class CONTENT_EXPORT FileSystemAccessDirectoryHandleImpl
|
||||
// is the implementation for passing create=true to GetDirectory.
|
||||
void GetDirectoryWithWritePermission(const storage::FileSystemURL& child_url,
|
||||
GetDirectoryCallback callback);
|
||||
void DidGetDirectory(const storage::FileSystemURL& url,
|
||||
void DidGetDirectory(storage::FileSystemURL child_url,
|
||||
GetDirectoryCallback callback,
|
||||
base::File::Error result);
|
||||
void DidReadDirectory(
|
||||
@ -122,6 +134,12 @@ class CONTENT_EXPORT FileSystemAccessDirectoryHandleImpl
|
||||
base::File::Error result,
|
||||
std::vector<filesystem::mojom::DirectoryEntry> file_list,
|
||||
bool has_more_entries);
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
void OnRemoveEntryContentUri(std::string basename,
|
||||
bool recurse,
|
||||
RemoveEntryCallback callback,
|
||||
base::FilePath child_path);
|
||||
#endif
|
||||
void RemoveEntryResolved(
|
||||
const std::string& basename,
|
||||
bool recurse,
|
||||
@ -150,6 +168,8 @@ class CONTENT_EXPORT FileSystemAccessDirectoryHandleImpl
|
||||
std::vector<blink::mojom::FileSystemAccessEntryPtr>)> final_callback,
|
||||
std::vector<blink::mojom::FileSystemAccessEntryPtr> entries);
|
||||
|
||||
storage::FileSystemURL CreateChildURL(const base::FilePath& child_path);
|
||||
|
||||
// Helper to create a blink::mojom::FileSystemAccessEntry struct.
|
||||
blink::mojom::FileSystemAccessEntryPtr CreateEntry(
|
||||
const base::SafeBaseName& basename,
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "url/gurl.h"
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
#include "base/android/content_uri_utils.h"
|
||||
#include "base/android/path_utils.h"
|
||||
#include "base/strings/escape.h"
|
||||
#include "base/test/android/content_uri_test_utils.h"
|
||||
@ -187,9 +188,14 @@ class FileSystemAccessFileHandleImplTest : public testing::Test {
|
||||
: base::FilePath::FromUTF8Unsafe("test");
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
if (use_content_uri) {
|
||||
base::FilePath content_uri =
|
||||
*base::test::android::GetContentUriFromCacheDirFilePath(
|
||||
test_file_path);
|
||||
base::FilePath parent =
|
||||
*base::test::android::GetInMemoryContentTreeUriFromCacheDirDirectory(
|
||||
dir_.GetPath());
|
||||
base::FilePath content_uri = base::ContentUriGetChildDocumentOrQuery(
|
||||
parent, test_file_path.BaseName().value(), "text/plain",
|
||||
/*is_directory=*/false,
|
||||
/*create=*/true);
|
||||
ASSERT_TRUE(base::ContentUriIsCreateChildDocumentQuery(content_uri));
|
||||
test_file_path = content_uri;
|
||||
}
|
||||
#endif
|
||||
|
@ -184,6 +184,18 @@ base::File NativeFileUtil::CreateOrOpen(const base::FilePath& path,
|
||||
|
||||
base::File::Error NativeFileUtil::EnsureFileExists(const base::FilePath& path,
|
||||
bool* created) {
|
||||
#if !BUILDFLAG(IS_ANDROID)
|
||||
if (!base::DirectoryExists(path.DirName())) {
|
||||
// If its parent does not exist, should return NOT_FOUND error.
|
||||
return base::File::FILE_ERROR_NOT_FOUND;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If |path| is a directory, return an error.
|
||||
if (base::DirectoryExists(path)) {
|
||||
return base::File::FILE_ERROR_NOT_A_FILE;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
if (path.IsContentUri()) {
|
||||
if (base::PathExists(path)) {
|
||||
@ -192,27 +204,17 @@ base::File::Error NativeFileUtil::EnsureFileExists(const base::FilePath& path,
|
||||
}
|
||||
return base::File::FILE_OK;
|
||||
}
|
||||
base::File file(path,
|
||||
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
|
||||
if (file.IsValid()) {
|
||||
if (created) {
|
||||
*created = true;
|
||||
}
|
||||
return base::File::FILE_OK;
|
||||
}
|
||||
base::FilePath result =
|
||||
base::ContentUriGetDocumentFromQuery(path, /*create=*/true);
|
||||
if (created) {
|
||||
*created = false;
|
||||
*created = !result.empty();
|
||||
}
|
||||
return base::File::FILE_ERROR_NOT_FOUND;
|
||||
if (result.empty()) {
|
||||
return base::File::FILE_ERROR_FAILED;
|
||||
}
|
||||
return base::File::FILE_OK;
|
||||
}
|
||||
#endif
|
||||
if (!base::DirectoryExists(path.DirName()))
|
||||
// If its parent does not exist, should return NOT_FOUND error.
|
||||
return base::File::FILE_ERROR_NOT_FOUND;
|
||||
|
||||
// If |path| is a directory, return an error.
|
||||
if (base::DirectoryExists(path))
|
||||
return base::File::FILE_ERROR_NOT_A_FILE;
|
||||
|
||||
// Tries to create the |path| exclusively. This should fail
|
||||
// with base::File::FILE_ERROR_EXISTS if the path already exists.
|
||||
@ -237,20 +239,39 @@ base::File::Error NativeFileUtil::EnsureFileExists(const base::FilePath& path,
|
||||
base::File::Error NativeFileUtil::CreateDirectory(const base::FilePath& path,
|
||||
bool exclusive,
|
||||
bool recursive) {
|
||||
#if !BUILDFLAG(IS_ANDROID)
|
||||
// If parent dir of file doesn't exist.
|
||||
if (!recursive && !base::PathExists(path.DirName()))
|
||||
return base::File::FILE_ERROR_NOT_FOUND;
|
||||
#endif
|
||||
|
||||
bool path_exists = base::PathExists(path);
|
||||
if (exclusive && path_exists)
|
||||
return base::File::FILE_ERROR_EXISTS;
|
||||
|
||||
// If file exists at the path.
|
||||
if (path_exists && !base::DirectoryExists(path))
|
||||
bool directory_exists = base::DirectoryExists(path);
|
||||
if (path_exists && !directory_exists) {
|
||||
return base::File::FILE_ERROR_NOT_A_DIRECTORY;
|
||||
}
|
||||
|
||||
if (!base::CreateDirectory(path))
|
||||
return base::File::FILE_ERROR_FAILED;
|
||||
if (!directory_exists) {
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
if (path.IsContentUri()) {
|
||||
base::FilePath result =
|
||||
base::ContentUriGetDocumentFromQuery(path, /*create=*/true);
|
||||
if (result.empty()) {
|
||||
return base::File::FILE_ERROR_FAILED;
|
||||
}
|
||||
} else if (!base::CreateDirectory(path)) {
|
||||
return base::File::FILE_ERROR_FAILED;
|
||||
}
|
||||
#else
|
||||
if (!base::CreateDirectory(path)) {
|
||||
return base::File::FILE_ERROR_FAILED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!SetPlatformSpecificDirectoryPermissions(path)) {
|
||||
// Since some file systems don't support permission setting, we do not treat
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
#include "base/android/content_uri_utils.h"
|
||||
#include "base/test/android/content_uri_test_utils.h"
|
||||
#endif
|
||||
|
||||
@ -125,9 +126,13 @@ TEST_F(NativeFileUtilTest, EnsureFileExists) {
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
// Delete file and recreate using content-URI rather than path.
|
||||
ASSERT_TRUE(base::DeleteFile(file_name));
|
||||
|
||||
base::FilePath content_uri =
|
||||
*base::test::android::GetContentUriFromCacheDirFilePath(file_name);
|
||||
base::FilePath parent =
|
||||
*base::test::android::GetInMemoryContentTreeUriFromCacheDirDirectory(
|
||||
Path());
|
||||
base::FilePath content_uri = base::ContentUriGetChildDocumentOrQuery(
|
||||
parent, "foobar", "text/plain", /*is_directory=*/false,
|
||||
/*create=*/true);
|
||||
ASSERT_FALSE(content_uri.empty());
|
||||
|
||||
EXPECT_EQ(base::File::FILE_OK,
|
||||
NativeFileUtil::EnsureFileExists(content_uri, &created));
|
||||
@ -158,6 +163,32 @@ TEST_F(NativeFileUtilTest, CreateAndDeleteDirectory) {
|
||||
ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::DeleteDirectory(dir_name));
|
||||
EXPECT_FALSE(base::DirectoryExists(dir_name));
|
||||
EXPECT_FALSE(NativeFileUtil::DirectoryExists(dir_name));
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
base::FilePath parent =
|
||||
*base::test::android::GetInMemoryContentTreeUriFromCacheDirDirectory(
|
||||
Path());
|
||||
base::FilePath query = base::ContentUriGetChildDocumentOrQuery(
|
||||
parent, "test_dir", "", /*is_directory=*/true, /*create=*/true);
|
||||
ASSERT_FALSE(query.empty());
|
||||
ASSERT_EQ(base::File::FILE_OK,
|
||||
NativeFileUtil::CreateDirectory(query, false /* exclusive */,
|
||||
false /* recursive */));
|
||||
base::FilePath content_uri =
|
||||
base::ContentUriGetDocumentFromQuery(query, /*create=*/false);
|
||||
ASSERT_FALSE(content_uri.empty());
|
||||
|
||||
EXPECT_TRUE(NativeFileUtil::DirectoryExists(content_uri));
|
||||
EXPECT_TRUE(base::DirectoryExists(dir_name));
|
||||
|
||||
ASSERT_EQ(base::File::FILE_ERROR_EXISTS,
|
||||
NativeFileUtil::CreateDirectory(content_uri, true /* exclusive */,
|
||||
false /* recursive */));
|
||||
|
||||
ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::DeleteDirectory(content_uri));
|
||||
EXPECT_FALSE(NativeFileUtil::DirectoryExists(content_uri));
|
||||
EXPECT_FALSE(base::DirectoryExists(dir_name));
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO(crbug.com/40511450): Remove this test once last_access_time has
|
||||
|
Reference in New Issue
Block a user