Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_DIR_READER_FALLBACK_H_
|
||||
#define BASE_FILES_DIR_READER_FALLBACK_H_
|
||||
|
||||
namespace base {
|
||||
|
||||
class DirReaderFallback {
|
||||
public:
|
||||
// Open a directory. If |IsValid| is true, then |Next| can be called to start
|
||||
// the iteration at the beginning of the directory.
|
||||
explicit DirReaderFallback(const char* directory_path) {}
|
||||
|
||||
// After construction, IsValid returns true iff the directory was
|
||||
// successfully opened.
|
||||
bool IsValid() const { return false; }
|
||||
|
||||
// Move to the next entry returning false if the iteration is complete.
|
||||
bool Next() { return false; }
|
||||
|
||||
// Return the name of the current directory entry.
|
||||
const char* name() { return nullptr;}
|
||||
|
||||
// Return the file descriptor which is being used.
|
||||
int fd() const { return -1; }
|
||||
|
||||
// Returns true if this is a no-op fallback class (for testing).
|
||||
static bool IsFallback() { return true; }
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_DIR_READER_FALLBACK_H_
|
||||
101
TMessagesProj/jni/voip/webrtc/base/files/dir_reader_linux.h
Normal file
101
TMessagesProj/jni/voip/webrtc/base/files/dir_reader_linux.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_DIR_READER_LINUX_H_
|
||||
#define BASE_FILES_DIR_READER_LINUX_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
|
||||
// See the comments in dir_reader_posix.h about this.
|
||||
|
||||
namespace base {
|
||||
|
||||
struct linux_dirent {
|
||||
uint64_t d_ino;
|
||||
int64_t d_off;
|
||||
unsigned short d_reclen;
|
||||
unsigned char d_type;
|
||||
char d_name[0];
|
||||
};
|
||||
|
||||
class DirReaderLinux {
|
||||
public:
|
||||
explicit DirReaderLinux(const char* directory_path)
|
||||
: fd_(open(directory_path, O_RDONLY | O_DIRECTORY)),
|
||||
offset_(0),
|
||||
size_(0) {
|
||||
memset(buf_, 0, sizeof(buf_));
|
||||
}
|
||||
|
||||
~DirReaderLinux() {
|
||||
if (fd_ >= 0) {
|
||||
if (IGNORE_EINTR(close(fd_)))
|
||||
RAW_LOG(ERROR, "Failed to close directory handle");
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return fd_ >= 0;
|
||||
}
|
||||
|
||||
// Move to the next entry returning false if the iteration is complete.
|
||||
bool Next() {
|
||||
if (size_) {
|
||||
linux_dirent* dirent = reinterpret_cast<linux_dirent*>(&buf_[offset_]);
|
||||
offset_ += dirent->d_reclen;
|
||||
}
|
||||
|
||||
if (offset_ != size_)
|
||||
return true;
|
||||
|
||||
const int r = syscall(__NR_getdents64, fd_, buf_, sizeof(buf_));
|
||||
if (r == 0)
|
||||
return false;
|
||||
if (r == -1) {
|
||||
DPLOG(FATAL) << "getdents64 failed";
|
||||
return false;
|
||||
}
|
||||
size_ = r;
|
||||
offset_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* name() const {
|
||||
if (!size_)
|
||||
return nullptr;
|
||||
|
||||
const linux_dirent* dirent =
|
||||
reinterpret_cast<const linux_dirent*>(&buf_[offset_]);
|
||||
return dirent->d_name;
|
||||
}
|
||||
|
||||
int fd() const {
|
||||
return fd_;
|
||||
}
|
||||
|
||||
static bool IsFallback() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int fd_;
|
||||
alignas(linux_dirent) unsigned char buf_[512];
|
||||
size_t offset_;
|
||||
size_t size_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DirReaderLinux);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_DIR_READER_LINUX_H_
|
||||
36
TMessagesProj/jni/voip/webrtc/base/files/dir_reader_posix.h
Normal file
36
TMessagesProj/jni/voip/webrtc/base/files/dir_reader_posix.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_DIR_READER_POSIX_H_
|
||||
#define BASE_FILES_DIR_READER_POSIX_H_
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
// This header provides a class, DirReaderPosix, which allows one to open and
|
||||
// read from directories without allocating memory. For the interface, see
|
||||
// the generic fallback in dir_reader_fallback.h.
|
||||
|
||||
// Mac note: OS X has getdirentries, but it only works if we restrict Chrome to
|
||||
// 32-bit inodes. There is a getdirentries64 syscall in 10.6, but it's not
|
||||
// wrapped and the direct syscall interface is unstable. Using an unstable API
|
||||
// seems worse than falling back to enumerating all file descriptors so we will
|
||||
// probably never implement this on the Mac.
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
#include "base/files/dir_reader_linux.h"
|
||||
#else
|
||||
#include "base/files/dir_reader_fallback.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
typedef DirReaderLinux DirReaderPosix;
|
||||
#else
|
||||
typedef DirReaderFallback DirReaderPosix;
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_DIR_READER_POSIX_H_
|
||||
166
TMessagesProj/jni/voip/webrtc/base/files/file.cc
Normal file
166
TMessagesProj/jni/voip/webrtc/base/files/file.cc
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_tracing.h"
|
||||
#include "base/metrics/histogram.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/timer/elapsed_timer.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
File::Info::Info() = default;
|
||||
|
||||
File::Info::~Info() = default;
|
||||
|
||||
File::File() = default;
|
||||
|
||||
#if !defined(OS_NACL)
|
||||
File::File(const FilePath& path, uint32_t flags) : error_details_(FILE_OK) {
|
||||
Initialize(path, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
File::File(ScopedPlatformFile platform_file)
|
||||
: File(std::move(platform_file), false) {}
|
||||
|
||||
File::File(PlatformFile platform_file) : File(platform_file, false) {}
|
||||
|
||||
File::File(ScopedPlatformFile platform_file, bool async)
|
||||
: file_(std::move(platform_file)), error_details_(FILE_OK), async_(async) {
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
DCHECK_GE(file_.get(), -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
File::File(PlatformFile platform_file, bool async)
|
||||
: file_(platform_file),
|
||||
error_details_(FILE_OK),
|
||||
async_(async) {
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
DCHECK_GE(platform_file, -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
File::File(Error error_details) : error_details_(error_details) {}
|
||||
|
||||
File::File(File&& other)
|
||||
: file_(other.TakePlatformFile()),
|
||||
tracing_path_(other.tracing_path_),
|
||||
error_details_(other.error_details()),
|
||||
created_(other.created()),
|
||||
async_(other.async_) {}
|
||||
|
||||
File::~File() {
|
||||
// Go through the AssertIOAllowed logic.
|
||||
Close();
|
||||
}
|
||||
|
||||
File& File::operator=(File&& other) {
|
||||
Close();
|
||||
SetPlatformFile(other.TakePlatformFile());
|
||||
tracing_path_ = other.tracing_path_;
|
||||
error_details_ = other.error_details();
|
||||
created_ = other.created();
|
||||
async_ = other.async_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if !defined(OS_NACL)
|
||||
void File::Initialize(const FilePath& path, uint32_t flags) {
|
||||
if (path.ReferencesParent()) {
|
||||
#if defined(OS_WIN)
|
||||
::SetLastError(ERROR_ACCESS_DENIED);
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
errno = EACCES;
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
error_details_ = FILE_ERROR_ACCESS_DENIED;
|
||||
return;
|
||||
}
|
||||
if (FileTracing::IsCategoryEnabled())
|
||||
tracing_path_ = path;
|
||||
SCOPED_FILE_TRACE("Initialize");
|
||||
DoInitialize(path, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool File::ReadAndCheck(int64_t offset, span<uint8_t> data) {
|
||||
int size = checked_cast<int>(data.size());
|
||||
return Read(offset, reinterpret_cast<char*>(data.data()), size) == size;
|
||||
}
|
||||
|
||||
bool File::ReadAtCurrentPosAndCheck(span<uint8_t> data) {
|
||||
int size = checked_cast<int>(data.size());
|
||||
return ReadAtCurrentPos(reinterpret_cast<char*>(data.data()), size) == size;
|
||||
}
|
||||
|
||||
bool File::WriteAndCheck(int64_t offset, span<const uint8_t> data) {
|
||||
int size = checked_cast<int>(data.size());
|
||||
return Write(offset, reinterpret_cast<const char*>(data.data()), size) ==
|
||||
size;
|
||||
}
|
||||
|
||||
bool File::WriteAtCurrentPosAndCheck(span<const uint8_t> data) {
|
||||
int size = checked_cast<int>(data.size());
|
||||
return WriteAtCurrentPos(reinterpret_cast<const char*>(data.data()), size) ==
|
||||
size;
|
||||
}
|
||||
|
||||
// static
|
||||
std::string File::ErrorToString(Error error) {
|
||||
switch (error) {
|
||||
case FILE_OK:
|
||||
return "FILE_OK";
|
||||
case FILE_ERROR_FAILED:
|
||||
return "FILE_ERROR_FAILED";
|
||||
case FILE_ERROR_IN_USE:
|
||||
return "FILE_ERROR_IN_USE";
|
||||
case FILE_ERROR_EXISTS:
|
||||
return "FILE_ERROR_EXISTS";
|
||||
case FILE_ERROR_NOT_FOUND:
|
||||
return "FILE_ERROR_NOT_FOUND";
|
||||
case FILE_ERROR_ACCESS_DENIED:
|
||||
return "FILE_ERROR_ACCESS_DENIED";
|
||||
case FILE_ERROR_TOO_MANY_OPENED:
|
||||
return "FILE_ERROR_TOO_MANY_OPENED";
|
||||
case FILE_ERROR_NO_MEMORY:
|
||||
return "FILE_ERROR_NO_MEMORY";
|
||||
case FILE_ERROR_NO_SPACE:
|
||||
return "FILE_ERROR_NO_SPACE";
|
||||
case FILE_ERROR_NOT_A_DIRECTORY:
|
||||
return "FILE_ERROR_NOT_A_DIRECTORY";
|
||||
case FILE_ERROR_INVALID_OPERATION:
|
||||
return "FILE_ERROR_INVALID_OPERATION";
|
||||
case FILE_ERROR_SECURITY:
|
||||
return "FILE_ERROR_SECURITY";
|
||||
case FILE_ERROR_ABORT:
|
||||
return "FILE_ERROR_ABORT";
|
||||
case FILE_ERROR_NOT_A_FILE:
|
||||
return "FILE_ERROR_NOT_A_FILE";
|
||||
case FILE_ERROR_NOT_EMPTY:
|
||||
return "FILE_ERROR_NOT_EMPTY";
|
||||
case FILE_ERROR_INVALID_URL:
|
||||
return "FILE_ERROR_INVALID_URL";
|
||||
case FILE_ERROR_IO:
|
||||
return "FILE_ERROR_IO";
|
||||
case FILE_ERROR_MAX:
|
||||
break;
|
||||
}
|
||||
|
||||
NOTREACHED();
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
402
TMessagesProj/jni/voip/webrtc/base/files/file.h
Normal file
402
TMessagesProj/jni/voip/webrtc/base/files/file.h
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_FILE_H_
|
||||
#define BASE_FILES_FILE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_tracing.h"
|
||||
#include "base/files/platform_file.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
|
||||
defined(OS_FUCHSIA) || (defined(OS_ANDROID) && __ANDROID_API__ < 21)
|
||||
typedef struct stat stat_wrapper_t;
|
||||
#elif defined(OS_POSIX)
|
||||
typedef struct stat64 stat_wrapper_t;
|
||||
#endif
|
||||
|
||||
// Thin wrapper around an OS-level file.
|
||||
// Note that this class does not provide any support for asynchronous IO, other
|
||||
// than the ability to create asynchronous handles on Windows.
|
||||
//
|
||||
// Note about const: this class does not attempt to determine if the underlying
|
||||
// file system object is affected by a particular method in order to consider
|
||||
// that method const or not. Only methods that deal with member variables in an
|
||||
// obvious non-modifying way are marked as const. Any method that forward calls
|
||||
// to the OS is not considered const, even if there is no apparent change to
|
||||
// member variables.
|
||||
class BASE_EXPORT File {
|
||||
public:
|
||||
// FLAG_(OPEN|CREATE).* are mutually exclusive. You should specify exactly one
|
||||
// of the five (possibly combining with other flags) when opening or creating
|
||||
// a file.
|
||||
// FLAG_(WRITE|APPEND) are mutually exclusive. This is so that APPEND behavior
|
||||
// will be consistent with O_APPEND on POSIX.
|
||||
// FLAG_EXCLUSIVE_(READ|WRITE) only grant exclusive access to the file on
|
||||
// creation on POSIX; for existing files, consider using Lock().
|
||||
enum Flags {
|
||||
FLAG_OPEN = 1 << 0, // Opens a file, only if it exists.
|
||||
FLAG_CREATE = 1 << 1, // Creates a new file, only if it does not
|
||||
// already exist.
|
||||
FLAG_OPEN_ALWAYS = 1 << 2, // May create a new file.
|
||||
FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file.
|
||||
FLAG_OPEN_TRUNCATED = 1 << 4, // Opens a file and truncates it, only if it
|
||||
// exists.
|
||||
FLAG_READ = 1 << 5,
|
||||
FLAG_WRITE = 1 << 6,
|
||||
FLAG_APPEND = 1 << 7,
|
||||
FLAG_EXCLUSIVE_READ = 1 << 8, // EXCLUSIVE is opposite of Windows SHARE.
|
||||
FLAG_EXCLUSIVE_WRITE = 1 << 9,
|
||||
FLAG_ASYNC = 1 << 10,
|
||||
FLAG_TEMPORARY = 1 << 11, // Used on Windows only.
|
||||
FLAG_HIDDEN = 1 << 12, // Used on Windows only.
|
||||
FLAG_DELETE_ON_CLOSE = 1 << 13,
|
||||
FLAG_WRITE_ATTRIBUTES = 1 << 14, // Used on Windows only.
|
||||
FLAG_SHARE_DELETE = 1 << 15, // Used on Windows only.
|
||||
FLAG_TERMINAL_DEVICE = 1 << 16, // Serial port flags.
|
||||
FLAG_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only.
|
||||
FLAG_EXECUTE = 1 << 18, // Used on Windows only.
|
||||
FLAG_SEQUENTIAL_SCAN = 1 << 19, // Used on Windows only.
|
||||
FLAG_CAN_DELETE_ON_CLOSE = 1 << 20, // Requests permission to delete a file
|
||||
// via DeleteOnClose() (Windows only).
|
||||
// See DeleteOnClose() for details.
|
||||
};
|
||||
|
||||
// This enum has been recorded in multiple histograms using PlatformFileError
|
||||
// enum. If the order of the fields needs to change, please ensure that those
|
||||
// histograms are obsolete or have been moved to a different enum.
|
||||
//
|
||||
// FILE_ERROR_ACCESS_DENIED is returned when a call fails because of a
|
||||
// filesystem restriction. FILE_ERROR_SECURITY is returned when a browser
|
||||
// policy doesn't allow the operation to be executed.
|
||||
enum Error {
|
||||
FILE_OK = 0,
|
||||
FILE_ERROR_FAILED = -1,
|
||||
FILE_ERROR_IN_USE = -2,
|
||||
FILE_ERROR_EXISTS = -3,
|
||||
FILE_ERROR_NOT_FOUND = -4,
|
||||
FILE_ERROR_ACCESS_DENIED = -5,
|
||||
FILE_ERROR_TOO_MANY_OPENED = -6,
|
||||
FILE_ERROR_NO_MEMORY = -7,
|
||||
FILE_ERROR_NO_SPACE = -8,
|
||||
FILE_ERROR_NOT_A_DIRECTORY = -9,
|
||||
FILE_ERROR_INVALID_OPERATION = -10,
|
||||
FILE_ERROR_SECURITY = -11,
|
||||
FILE_ERROR_ABORT = -12,
|
||||
FILE_ERROR_NOT_A_FILE = -13,
|
||||
FILE_ERROR_NOT_EMPTY = -14,
|
||||
FILE_ERROR_INVALID_URL = -15,
|
||||
FILE_ERROR_IO = -16,
|
||||
// Put new entries here and increment FILE_ERROR_MAX.
|
||||
FILE_ERROR_MAX = -17
|
||||
};
|
||||
|
||||
// This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux.
|
||||
enum Whence {
|
||||
FROM_BEGIN = 0,
|
||||
FROM_CURRENT = 1,
|
||||
FROM_END = 2
|
||||
};
|
||||
|
||||
// Used to hold information about a given file.
|
||||
// If you add more fields to this structure (platform-specific fields are OK),
|
||||
// make sure to update all functions that use it in file_util_{win|posix}.cc,
|
||||
// too, and the ParamTraits<base::File::Info> implementation in
|
||||
// ipc/ipc_message_utils.cc.
|
||||
struct BASE_EXPORT Info {
|
||||
Info();
|
||||
~Info();
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// Fills this struct with values from |stat_info|.
|
||||
void FromStat(const stat_wrapper_t& stat_info);
|
||||
#endif
|
||||
|
||||
// The size of the file in bytes. Undefined when is_directory is true.
|
||||
int64_t size = 0;
|
||||
|
||||
// True if the file corresponds to a directory.
|
||||
bool is_directory = false;
|
||||
|
||||
// True if the file corresponds to a symbolic link. For Windows currently
|
||||
// not supported and thus always false.
|
||||
bool is_symbolic_link = false;
|
||||
|
||||
// The last modified time of a file.
|
||||
Time last_modified;
|
||||
|
||||
// The last accessed time of a file.
|
||||
Time last_accessed;
|
||||
|
||||
// The creation time of a file.
|
||||
Time creation_time;
|
||||
};
|
||||
|
||||
File();
|
||||
|
||||
// Creates or opens the given file. This will fail with 'access denied' if the
|
||||
// |path| contains path traversal ('..') components.
|
||||
File(const FilePath& path, uint32_t flags);
|
||||
|
||||
// Takes ownership of |platform_file| and sets async to false.
|
||||
explicit File(ScopedPlatformFile platform_file);
|
||||
explicit File(PlatformFile platform_file);
|
||||
|
||||
// Takes ownership of |platform_file| and sets async to the given value.
|
||||
// This constructor exists because on Windows you can't check if platform_file
|
||||
// is async or not.
|
||||
File(ScopedPlatformFile platform_file, bool async);
|
||||
File(PlatformFile platform_file, bool async);
|
||||
|
||||
// Creates an object with a specific error_details code.
|
||||
explicit File(Error error_details);
|
||||
|
||||
File(File&& other);
|
||||
|
||||
~File();
|
||||
|
||||
File& operator=(File&& other);
|
||||
|
||||
// Creates or opens the given file.
|
||||
void Initialize(const FilePath& path, uint32_t flags);
|
||||
|
||||
// Returns |true| if the handle / fd wrapped by this object is valid. This
|
||||
// method doesn't interact with the file system (and is safe to be called from
|
||||
// ThreadRestrictions::SetIOAllowed(false) threads).
|
||||
bool IsValid() const;
|
||||
|
||||
// Returns true if a new file was created (or an old one truncated to zero
|
||||
// length to simulate a new file, which can happen with
|
||||
// FLAG_CREATE_ALWAYS), and false otherwise.
|
||||
bool created() const { return created_; }
|
||||
|
||||
// Returns the OS result of opening this file. Note that the way to verify
|
||||
// the success of the operation is to use IsValid(), not this method:
|
||||
// File file(path, flags);
|
||||
// if (!file.IsValid())
|
||||
// return;
|
||||
Error error_details() const { return error_details_; }
|
||||
|
||||
PlatformFile GetPlatformFile() const;
|
||||
PlatformFile TakePlatformFile();
|
||||
|
||||
// Destroying this object closes the file automatically.
|
||||
void Close();
|
||||
|
||||
// Changes current position in the file to an |offset| relative to an origin
|
||||
// defined by |whence|. Returns the resultant current position in the file
|
||||
// (relative to the start) or -1 in case of error.
|
||||
int64_t Seek(Whence whence, int64_t offset);
|
||||
|
||||
// Simplified versions of Read() and friends (see below) that check the int
|
||||
// return value and just return a boolean. They return true if and only if
|
||||
// the function read in / wrote out exactly |size| bytes of data.
|
||||
bool ReadAndCheck(int64_t offset, span<uint8_t> data);
|
||||
bool ReadAtCurrentPosAndCheck(span<uint8_t> data);
|
||||
bool WriteAndCheck(int64_t offset, span<const uint8_t> data);
|
||||
bool WriteAtCurrentPosAndCheck(span<const uint8_t> data);
|
||||
|
||||
// Reads the given number of bytes (or until EOF is reached) starting with the
|
||||
// given offset. Returns the number of bytes read, or -1 on error. Note that
|
||||
// this function makes a best effort to read all data on all platforms, so it
|
||||
// is not intended for stream oriented files but instead for cases when the
|
||||
// normal expectation is that actually |size| bytes are read unless there is
|
||||
// an error.
|
||||
int Read(int64_t offset, char* data, int size);
|
||||
|
||||
// Same as above but without seek.
|
||||
int ReadAtCurrentPos(char* data, int size);
|
||||
|
||||
// Reads the given number of bytes (or until EOF is reached) starting with the
|
||||
// given offset, but does not make any effort to read all data on all
|
||||
// platforms. Returns the number of bytes read, or -1 on error.
|
||||
int ReadNoBestEffort(int64_t offset, char* data, int size);
|
||||
|
||||
// Same as above but without seek.
|
||||
int ReadAtCurrentPosNoBestEffort(char* data, int size);
|
||||
|
||||
// Writes the given buffer into the file at the given offset, overwritting any
|
||||
// data that was previously there. Returns the number of bytes written, or -1
|
||||
// on error. Note that this function makes a best effort to write all data on
|
||||
// all platforms. |data| can be nullptr when |size| is 0.
|
||||
// Ignores the offset and writes to the end of the file if the file was opened
|
||||
// with FLAG_APPEND.
|
||||
int Write(int64_t offset, const char* data, int size);
|
||||
|
||||
// Save as above but without seek.
|
||||
int WriteAtCurrentPos(const char* data, int size);
|
||||
|
||||
// Save as above but does not make any effort to write all data on all
|
||||
// platforms. Returns the number of bytes written, or -1 on error.
|
||||
int WriteAtCurrentPosNoBestEffort(const char* data, int size);
|
||||
|
||||
// Returns the current size of this file, or a negative number on failure.
|
||||
int64_t GetLength();
|
||||
|
||||
// Truncates the file to the given length. If |length| is greater than the
|
||||
// current size of the file, the file is extended with zeros. If the file
|
||||
// doesn't exist, |false| is returned.
|
||||
bool SetLength(int64_t length);
|
||||
|
||||
// Instructs the filesystem to flush the file to disk. (POSIX: fsync, Windows:
|
||||
// FlushFileBuffers).
|
||||
// Calling Flush() does not guarantee file integrity and thus is not a valid
|
||||
// substitute for file integrity checks and recovery codepaths for malformed
|
||||
// files. It can also be *really* slow, so avoid blocking on Flush(),
|
||||
// especially please don't block shutdown on Flush().
|
||||
// Latency percentiles of Flush() across all platforms as of July 2016:
|
||||
// 50 % > 5 ms
|
||||
// 10 % > 58 ms
|
||||
// 1 % > 357 ms
|
||||
// 0.1 % > 1.8 seconds
|
||||
// 0.01 % > 7.6 seconds
|
||||
bool Flush();
|
||||
|
||||
// Updates the file times.
|
||||
bool SetTimes(Time last_access_time, Time last_modified_time);
|
||||
|
||||
// Returns some basic information for the given file.
|
||||
bool GetInfo(Info* info);
|
||||
|
||||
#if !defined(OS_FUCHSIA) // Fuchsia's POSIX API does not support file locking.
|
||||
enum class LockMode {
|
||||
kShared,
|
||||
kExclusive,
|
||||
};
|
||||
|
||||
// Attempts to take an exclusive write lock on the file. Returns immediately
|
||||
// (i.e. does not wait for another process to unlock the file). If the lock
|
||||
// was obtained, the result will be FILE_OK. A lock only guarantees
|
||||
// that other processes may not also take a lock on the same file with the
|
||||
// same API - it may still be opened, renamed, unlinked, etc.
|
||||
//
|
||||
// Common semantics:
|
||||
// * Locks are held by processes, but not inherited by child processes.
|
||||
// * Locks are released by the OS on file close or process termination.
|
||||
// * Locks are reliable only on local filesystems.
|
||||
// * Duplicated file handles may also write to locked files.
|
||||
// Windows-specific semantics:
|
||||
// * Locks are mandatory for read/write APIs, advisory for mapping APIs.
|
||||
// * Within a process, locking the same file (by the same or new handle)
|
||||
// will fail.
|
||||
// POSIX-specific semantics:
|
||||
// * Locks are advisory only.
|
||||
// * Within a process, locking the same file (by the same or new handle)
|
||||
// will succeed. The new lock replaces the old lock.
|
||||
// * Closing any descriptor on a given file releases the lock.
|
||||
Error Lock(LockMode mode = LockMode::kExclusive);
|
||||
|
||||
// Unlock a file previously locked.
|
||||
Error Unlock();
|
||||
|
||||
#endif // !defined(OS_FUCHSIA)
|
||||
|
||||
// Returns a new object referencing this file for use within the current
|
||||
// process. Handling of FLAG_DELETE_ON_CLOSE varies by OS. On POSIX, the File
|
||||
// object that was created or initialized with this flag will have unlinked
|
||||
// the underlying file when it was created or opened. On Windows, the
|
||||
// underlying file is deleted when the last handle to it is closed.
|
||||
File Duplicate() const;
|
||||
|
||||
bool async() const { return async_; }
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Sets or clears the DeleteFile disposition on the file. Returns true if
|
||||
// the disposition was set or cleared, as indicated by |delete_on_close|.
|
||||
//
|
||||
// Microsoft Windows deletes a file only when the DeleteFile disposition is
|
||||
// set on a file when the last handle to the last underlying kernel File
|
||||
// object is closed. This disposition is be set by:
|
||||
// - Calling the Win32 DeleteFile function with the path to a file.
|
||||
// - Opening/creating a file with FLAG_DELETE_ON_CLOSE and then closing all
|
||||
// handles to that File object.
|
||||
// - Opening/creating a file with FLAG_CAN_DELETE_ON_CLOSE and subsequently
|
||||
// calling DeleteOnClose(true).
|
||||
//
|
||||
// In all cases, all pre-existing handles to the file must have been opened
|
||||
// with FLAG_SHARE_DELETE. Once the disposition has been set by any of the
|
||||
// above means, no new File objects can be created for the file.
|
||||
//
|
||||
// So:
|
||||
// - Use FLAG_SHARE_DELETE when creating/opening a file to allow another
|
||||
// entity on the system to cause it to be deleted when it is closed. (Note:
|
||||
// another entity can delete the file the moment after it is closed, so not
|
||||
// using this permission doesn't provide any protections.)
|
||||
// - Use FLAG_DELETE_ON_CLOSE for any file that is to be deleted after use.
|
||||
// The OS will ensure it is deleted even in the face of process termination.
|
||||
// Note that it's possible for deletion to be cancelled via another File
|
||||
// object referencing the same file using DeleteOnClose(false) to clear the
|
||||
// DeleteFile disposition after the original File is closed.
|
||||
// - Use FLAG_CAN_DELETE_ON_CLOSE in conjunction with DeleteOnClose() to alter
|
||||
// the DeleteFile disposition on an open handle. This fine-grained control
|
||||
// allows for marking a file for deletion during processing so that it is
|
||||
// deleted in the event of untimely process termination, and then clearing
|
||||
// this state once the file is suitable for persistence.
|
||||
bool DeleteOnClose(bool delete_on_close);
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
static Error OSErrorToFileError(DWORD last_error);
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
static Error OSErrorToFileError(int saved_errno);
|
||||
#endif
|
||||
|
||||
// Gets the last global error (errno or GetLastError()) and converts it to the
|
||||
// closest base::File::Error equivalent via OSErrorToFileError(). The returned
|
||||
// value is only trustworthy immediately after another base::File method
|
||||
// fails. base::File never resets the global error to zero.
|
||||
static Error GetLastFileError();
|
||||
|
||||
// Converts an error value to a human-readable form. Used for logging.
|
||||
static std::string ErrorToString(Error error);
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// Wrapper for stat() or stat64().
|
||||
static int Stat(const char* path, stat_wrapper_t* sb);
|
||||
static int Fstat(int fd, stat_wrapper_t* sb);
|
||||
static int Lstat(const char* path, stat_wrapper_t* sb);
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class FileTracing::ScopedTrace;
|
||||
|
||||
// Creates or opens the given file. Only called if |path| has no
|
||||
// traversal ('..') components.
|
||||
void DoInitialize(const FilePath& path, uint32_t flags);
|
||||
|
||||
void SetPlatformFile(PlatformFile file);
|
||||
|
||||
ScopedPlatformFile file_;
|
||||
|
||||
// A path to use for tracing purposes. Set if file tracing is enabled during
|
||||
// |Initialize()|.
|
||||
FilePath tracing_path_;
|
||||
|
||||
// Object tied to the lifetime of |this| that enables/disables tracing.
|
||||
FileTracing::ScopedEnabler trace_enabler_;
|
||||
|
||||
Error error_details_ = FILE_ERROR_FAILED;
|
||||
bool created_ = false;
|
||||
bool async_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(File);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_FILE_H_
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_descriptor_watcher_posix.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/callback_helpers.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/message_loop/message_loop_current.h"
|
||||
#include "base/message_loop/message_pump_for_io.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/sequenced_task_runner.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
#include "base/threading/thread_local.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
// Per-thread FileDescriptorWatcher registration.
|
||||
ThreadLocalPointer<FileDescriptorWatcher>& GetTlsFdWatcher() {
|
||||
static NoDestructor<ThreadLocalPointer<FileDescriptorWatcher>> tls_fd_watcher;
|
||||
return *tls_fd_watcher;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class FileDescriptorWatcher::Controller::Watcher
|
||||
: public MessagePumpForIO::FdWatcher,
|
||||
public MessageLoopCurrent::DestructionObserver {
|
||||
public:
|
||||
Watcher(WeakPtr<Controller> controller, MessagePumpForIO::Mode mode, int fd);
|
||||
~Watcher() override;
|
||||
|
||||
void StartWatching();
|
||||
|
||||
private:
|
||||
friend class FileDescriptorWatcher;
|
||||
|
||||
// MessagePumpForIO::FdWatcher:
|
||||
void OnFileCanReadWithoutBlocking(int fd) override;
|
||||
void OnFileCanWriteWithoutBlocking(int fd) override;
|
||||
|
||||
// MessageLoopCurrent::DestructionObserver:
|
||||
void WillDestroyCurrentMessageLoop() override;
|
||||
|
||||
// The MessagePumpForIO's watch handle (stops the watch when destroyed).
|
||||
MessagePumpForIO::FdWatchController fd_watch_controller_;
|
||||
|
||||
// Runs tasks on the sequence on which this was instantiated (i.e. the
|
||||
// sequence on which the callback must run).
|
||||
const scoped_refptr<SequencedTaskRunner> callback_task_runner_ =
|
||||
SequencedTaskRunnerHandle::Get();
|
||||
|
||||
// The Controller that created this Watcher. This WeakPtr is bound to the
|
||||
// |controller_| thread and can only be used by this Watcher to post back to
|
||||
// |callback_task_runner_|.
|
||||
WeakPtr<Controller> controller_;
|
||||
|
||||
// Whether this Watcher is notified when |fd_| becomes readable or writable
|
||||
// without blocking.
|
||||
const MessagePumpForIO::Mode mode_;
|
||||
|
||||
// The watched file descriptor.
|
||||
const int fd_;
|
||||
|
||||
// Except for the constructor, every method of this class must run on the same
|
||||
// MessagePumpForIO thread.
|
||||
ThreadChecker thread_checker_;
|
||||
|
||||
// Whether this Watcher was registered as a DestructionObserver on the
|
||||
// MessagePumpForIO thread.
|
||||
bool registered_as_destruction_observer_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Watcher);
|
||||
};
|
||||
|
||||
FileDescriptorWatcher::Controller::Watcher::Watcher(
|
||||
WeakPtr<Controller> controller,
|
||||
MessagePumpForIO::Mode mode,
|
||||
int fd)
|
||||
: fd_watch_controller_(FROM_HERE),
|
||||
controller_(controller),
|
||||
mode_(mode),
|
||||
fd_(fd) {
|
||||
DCHECK(callback_task_runner_);
|
||||
thread_checker_.DetachFromThread();
|
||||
}
|
||||
|
||||
FileDescriptorWatcher::Controller::Watcher::~Watcher() {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
MessageLoopCurrentForIO::Get()->RemoveDestructionObserver(this);
|
||||
}
|
||||
|
||||
void FileDescriptorWatcher::Controller::Watcher::StartWatching() {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
DCHECK(MessageLoopCurrentForIO::IsSet());
|
||||
|
||||
const bool watch_success =
|
||||
MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
|
||||
fd_, false, mode_, &fd_watch_controller_, this);
|
||||
DCHECK(watch_success) << "Failed to watch fd=" << fd_;
|
||||
|
||||
if (!registered_as_destruction_observer_) {
|
||||
MessageLoopCurrentForIO::Get()->AddDestructionObserver(this);
|
||||
registered_as_destruction_observer_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FileDescriptorWatcher::Controller::Watcher::OnFileCanReadWithoutBlocking(
|
||||
int fd) {
|
||||
DCHECK_EQ(fd_, fd);
|
||||
DCHECK_EQ(MessagePumpForIO::WATCH_READ, mode_);
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
|
||||
// Run the callback on the sequence on which the watch was initiated.
|
||||
callback_task_runner_->PostTask(
|
||||
FROM_HERE, BindOnce(&Controller::RunCallback, controller_));
|
||||
}
|
||||
|
||||
void FileDescriptorWatcher::Controller::Watcher::OnFileCanWriteWithoutBlocking(
|
||||
int fd) {
|
||||
DCHECK_EQ(fd_, fd);
|
||||
DCHECK_EQ(MessagePumpForIO::WATCH_WRITE, mode_);
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
|
||||
// Run the callback on the sequence on which the watch was initiated.
|
||||
callback_task_runner_->PostTask(
|
||||
FROM_HERE, BindOnce(&Controller::RunCallback, controller_));
|
||||
}
|
||||
|
||||
void FileDescriptorWatcher::Controller::Watcher::
|
||||
WillDestroyCurrentMessageLoop() {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
|
||||
if (callback_task_runner_->RunsTasksInCurrentSequence()) {
|
||||
// |controller_| can be accessed directly when Watcher runs on the same
|
||||
// thread.
|
||||
controller_->watcher_.reset();
|
||||
} else {
|
||||
// If the Watcher and the Controller live on different threads, delete
|
||||
// |this| synchronously. Pending tasks bound to an unretained Watcher* will
|
||||
// not run since this loop is dead. The associated Controller still
|
||||
// technically owns this via unique_ptr but it never uses it directly and
|
||||
// will ultimately send it to this thread for deletion (and that also won't
|
||||
// run since the loop being dead).
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
FileDescriptorWatcher::Controller::Controller(MessagePumpForIO::Mode mode,
|
||||
int fd,
|
||||
const RepeatingClosure& callback)
|
||||
: callback_(callback),
|
||||
io_thread_task_runner_(GetTlsFdWatcher().Get()->io_thread_task_runner()) {
|
||||
DCHECK(!callback_.is_null());
|
||||
DCHECK(io_thread_task_runner_);
|
||||
watcher_ = std::make_unique<Watcher>(weak_factory_.GetWeakPtr(), mode, fd);
|
||||
StartWatching();
|
||||
}
|
||||
|
||||
FileDescriptorWatcher::Controller::~Controller() {
|
||||
DCHECK(sequence_checker_.CalledOnValidSequence());
|
||||
|
||||
if (io_thread_task_runner_->BelongsToCurrentThread()) {
|
||||
// If the MessagePumpForIO and the Controller live on the same thread.
|
||||
watcher_.reset();
|
||||
} else {
|
||||
// Synchronously wait until |watcher_| is deleted on the MessagePumpForIO
|
||||
// thread. This ensures that the file descriptor is never accessed after
|
||||
// this destructor returns.
|
||||
//
|
||||
// Use a ScopedClosureRunner to ensure that |done| is signaled even if the
|
||||
// thread doesn't run any more tasks (if PostTask returns true, it means
|
||||
// that the task was queued, but it doesn't mean that a RunLoop will run the
|
||||
// task before the queue is deleted).
|
||||
//
|
||||
// We considered associating "generations" to file descriptors to avoid the
|
||||
// synchronous wait. For example, if the IO thread gets a "cancel" for fd=6,
|
||||
// generation=1 after getting a "start watching" for fd=6, generation=2, it
|
||||
// can ignore the "Cancel". However, "generations" didn't solve this race:
|
||||
//
|
||||
// T1 (client) Start watching fd = 6 with WatchReadable()
|
||||
// Stop watching fd = 6
|
||||
// Close fd = 6
|
||||
// Open a new file, fd = 6 gets reused.
|
||||
// T2 (io) Watcher::StartWatching()
|
||||
// Incorrectly starts watching fd = 6 which now refers to a
|
||||
// different file than when WatchReadable() was called.
|
||||
WaitableEvent done;
|
||||
io_thread_task_runner_->PostTask(
|
||||
FROM_HERE, BindOnce(
|
||||
[](Watcher* watcher, ScopedClosureRunner closure) {
|
||||
// Since |watcher| is a raw pointer, it isn't deleted
|
||||
// if this callback is deleted before it gets to run.
|
||||
delete watcher;
|
||||
// |closure| runs at the end of this scope.
|
||||
},
|
||||
Unretained(watcher_.release()),
|
||||
ScopedClosureRunner(BindOnce(&WaitableEvent::Signal,
|
||||
Unretained(&done)))));
|
||||
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow;
|
||||
done.Wait();
|
||||
}
|
||||
|
||||
// Since WeakPtrs are invalidated by the destructor, any pending RunCallback()
|
||||
// won't be invoked after this returns.
|
||||
}
|
||||
|
||||
void FileDescriptorWatcher::Controller::StartWatching() {
|
||||
DCHECK(sequence_checker_.CalledOnValidSequence());
|
||||
if (io_thread_task_runner_->BelongsToCurrentThread()) {
|
||||
// If the MessagePumpForIO and the Controller live on the same thread.
|
||||
watcher_->StartWatching();
|
||||
} else {
|
||||
// It is safe to use Unretained() below because |watcher_| can only be
|
||||
// deleted by a delete task posted to |io_thread_task_runner_| by this
|
||||
// Controller's destructor. Since this delete task hasn't been posted yet,
|
||||
// it can't run before the task posted below.
|
||||
io_thread_task_runner_->PostTask(
|
||||
FROM_HERE,
|
||||
BindOnce(&Watcher::StartWatching, Unretained(watcher_.get())));
|
||||
}
|
||||
}
|
||||
|
||||
void FileDescriptorWatcher::Controller::RunCallback() {
|
||||
DCHECK(sequence_checker_.CalledOnValidSequence());
|
||||
|
||||
WeakPtr<Controller> weak_this = weak_factory_.GetWeakPtr();
|
||||
|
||||
callback_.Run();
|
||||
|
||||
// If |this| wasn't deleted, re-enable the watch.
|
||||
if (weak_this)
|
||||
StartWatching();
|
||||
}
|
||||
|
||||
FileDescriptorWatcher::FileDescriptorWatcher(
|
||||
scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner)
|
||||
: io_thread_task_runner_(std::move(io_thread_task_runner)) {
|
||||
DCHECK(!GetTlsFdWatcher().Get());
|
||||
GetTlsFdWatcher().Set(this);
|
||||
}
|
||||
|
||||
FileDescriptorWatcher::~FileDescriptorWatcher() {
|
||||
GetTlsFdWatcher().Set(nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<FileDescriptorWatcher::Controller>
|
||||
FileDescriptorWatcher::WatchReadable(int fd, const RepeatingClosure& callback) {
|
||||
return WrapUnique(new Controller(MessagePumpForIO::WATCH_READ, fd, callback));
|
||||
}
|
||||
|
||||
std::unique_ptr<FileDescriptorWatcher::Controller>
|
||||
FileDescriptorWatcher::WatchWritable(int fd, const RepeatingClosure& callback) {
|
||||
return WrapUnique(
|
||||
new Controller(MessagePumpForIO::WATCH_WRITE, fd, callback));
|
||||
}
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
void FileDescriptorWatcher::AssertAllowed() {
|
||||
DCHECK(GetTlsFdWatcher().Get());
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_FILE_DESCRIPTOR_WATCHER_POSIX_H_
|
||||
#define BASE_FILES_FILE_DESCRIPTOR_WATCHER_POSIX_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/message_loop/message_pump_for_io.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class SingleThreadTaskRunner;
|
||||
|
||||
// The FileDescriptorWatcher API allows callbacks to be invoked when file
|
||||
// descriptors are readable or writable without blocking.
|
||||
//
|
||||
// To enable this API in unit tests, use a TaskEnvironment with
|
||||
// MainThreadType::IO.
|
||||
//
|
||||
// Note: Prefer FileDescriptorWatcher to MessageLoopForIO::WatchFileDescriptor()
|
||||
// for non-critical IO. FileDescriptorWatcher works on threads/sequences without
|
||||
// MessagePumps but involves going through the task queue after being notified
|
||||
// by the OS (a desirablable property for non-critical IO that shouldn't preempt
|
||||
// the main queue).
|
||||
class BASE_EXPORT FileDescriptorWatcher {
|
||||
public:
|
||||
// Instantiated and returned by WatchReadable() or WatchWritable(). The
|
||||
// constructor registers a callback to be invoked when a file descriptor is
|
||||
// readable or writable without blocking and the destructor unregisters it.
|
||||
class Controller {
|
||||
public:
|
||||
// Unregisters the callback registered by the constructor.
|
||||
~Controller();
|
||||
|
||||
private:
|
||||
friend class FileDescriptorWatcher;
|
||||
class Watcher;
|
||||
|
||||
// Registers |callback| to be invoked when |fd| is readable or writable
|
||||
// without blocking (depending on |mode|).
|
||||
Controller(MessagePumpForIO::Mode mode,
|
||||
int fd,
|
||||
const RepeatingClosure& callback);
|
||||
|
||||
// Starts watching the file descriptor.
|
||||
void StartWatching();
|
||||
|
||||
// Runs |callback_|.
|
||||
void RunCallback();
|
||||
|
||||
// The callback to run when the watched file descriptor is readable or
|
||||
// writable without blocking.
|
||||
RepeatingClosure callback_;
|
||||
|
||||
// TaskRunner associated with the MessageLoopForIO that watches the file
|
||||
// descriptor.
|
||||
const scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner_;
|
||||
|
||||
// Notified by the MessageLoopForIO associated with
|
||||
// |io_thread_task_runner_| when the watched file descriptor is
|
||||
// readable or writable without blocking. Posts a task to run RunCallback()
|
||||
// on the sequence on which the Controller was instantiated. When the
|
||||
// Controller is deleted, ownership of |watcher_| is transfered to a delete
|
||||
// task posted to the MessageLoopForIO. This ensures that |watcher_| isn't
|
||||
// deleted while it is being used by the MessageLoopForIO.
|
||||
std::unique_ptr<Watcher> watcher_;
|
||||
|
||||
// Validates that the Controller is used on the sequence on which it was
|
||||
// instantiated.
|
||||
SequenceChecker sequence_checker_;
|
||||
|
||||
WeakPtrFactory<Controller> weak_factory_{this};
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Controller);
|
||||
};
|
||||
|
||||
// Registers |io_thread_task_runner| to watch file descriptors for which
|
||||
// callbacks are registered from the current thread via WatchReadable() or
|
||||
// WatchWritable(). |io_thread_task_runner| must post tasks to a thread which
|
||||
// runs a MessagePumpForIO. If it is not the current thread, it must be highly
|
||||
// responsive (i.e. not used to run other expensive tasks such as potentially
|
||||
// blocking I/O) since ~Controller waits for a task posted to it.
|
||||
explicit FileDescriptorWatcher(
|
||||
scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner);
|
||||
~FileDescriptorWatcher();
|
||||
|
||||
// Registers |callback| to be posted on the current sequence when |fd| is
|
||||
// readable or writable without blocking. |callback| is unregistered when the
|
||||
// returned Controller is deleted (deletion must happen on the current
|
||||
// sequence).
|
||||
// Usage note: To call these methods, a FileDescriptorWatcher must have been
|
||||
// instantiated on the current thread and SequencedTaskRunnerHandle::IsSet()
|
||||
// must return true (these conditions are met at least on all ThreadPool
|
||||
// threads as well as on threads backed by a MessageLoopForIO). |fd| must
|
||||
// outlive the returned Controller.
|
||||
// Shutdown note: notifications aren't guaranteed to be emitted once the bound
|
||||
// (current) SequencedTaskRunner enters its shutdown phase (i.e.
|
||||
// ThreadPool::Shutdown() or Thread::Stop()) regardless of the
|
||||
// SequencedTaskRunner's TaskShutdownBehavior.
|
||||
static std::unique_ptr<Controller> WatchReadable(
|
||||
int fd,
|
||||
const RepeatingClosure& callback);
|
||||
static std::unique_ptr<Controller> WatchWritable(
|
||||
int fd,
|
||||
const RepeatingClosure& callback);
|
||||
|
||||
// Asserts that usage of this API is allowed on this thread.
|
||||
static void AssertAllowed()
|
||||
#if DCHECK_IS_ON()
|
||||
;
|
||||
#else
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner() const {
|
||||
return io_thread_task_runner_;
|
||||
}
|
||||
|
||||
const scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_FILE_DESCRIPTOR_WATCHER_POSIX_H_
|
||||
25
TMessagesProj/jni/voip/webrtc/base/files/file_enumerator.cc
Normal file
25
TMessagesProj/jni/voip/webrtc/base/files/file_enumerator.cc
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_enumerator.h"
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
FileEnumerator::FileInfo::~FileInfo() = default;
|
||||
|
||||
bool FileEnumerator::ShouldSkip(const FilePath& path) {
|
||||
FilePath::StringType basename = path.BaseName().value();
|
||||
return basename == FILE_PATH_LITERAL(".") ||
|
||||
(basename == FILE_PATH_LITERAL("..") &&
|
||||
!(INCLUDE_DOT_DOT & file_type_));
|
||||
}
|
||||
|
||||
bool FileEnumerator::IsTypeMatched(bool is_dir) const {
|
||||
return (file_type_ &
|
||||
(is_dir ? FileEnumerator::DIRECTORIES : FileEnumerator::FILES)) != 0;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
208
TMessagesProj/jni/voip/webrtc/base/files/file_enumerator.h
Normal file
208
TMessagesProj/jni/voip/webrtc/base/files/file_enumerator.h
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_FILE_ENUMERATOR_H_
|
||||
#define BASE_FILES_FILE_ENUMERATOR_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/containers/stack.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
#include <unistd.h>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "base/files/file.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
// A class for enumerating the files in a provided path. The order of the
|
||||
// results is not guaranteed.
|
||||
//
|
||||
// This is blocking. Do not use on critical threads.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// base::FileEnumerator enum(my_dir, false, base::FileEnumerator::FILES,
|
||||
// FILE_PATH_LITERAL("*.txt"));
|
||||
// for (base::FilePath name = enum.Next(); !name.empty(); name = enum.Next())
|
||||
// ...
|
||||
class BASE_EXPORT FileEnumerator {
|
||||
public:
|
||||
// Note: copy & assign supported.
|
||||
class BASE_EXPORT FileInfo {
|
||||
public:
|
||||
FileInfo();
|
||||
~FileInfo();
|
||||
|
||||
bool IsDirectory() const;
|
||||
|
||||
// The name of the file. This will not include any path information. This
|
||||
// is in constrast to the value returned by FileEnumerator.Next() which
|
||||
// includes the |root_path| passed into the FileEnumerator constructor.
|
||||
FilePath GetName() const;
|
||||
|
||||
int64_t GetSize() const;
|
||||
Time GetLastModifiedTime() const;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Note that the cAlternateFileName (used to hold the "short" 8.3 name)
|
||||
// of the WIN32_FIND_DATA will be empty. Since we don't use short file
|
||||
// names, we tell Windows to omit it which speeds up the query slightly.
|
||||
const WIN32_FIND_DATA& find_data() const { return find_data_; }
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
const stat_wrapper_t& stat() const { return stat_; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class FileEnumerator;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
WIN32_FIND_DATA find_data_;
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
stat_wrapper_t stat_;
|
||||
FilePath filename_;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum FileType {
|
||||
FILES = 1 << 0,
|
||||
DIRECTORIES = 1 << 1,
|
||||
INCLUDE_DOT_DOT = 1 << 2,
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
SHOW_SYM_LINKS = 1 << 4,
|
||||
#endif
|
||||
};
|
||||
|
||||
// Search policy for intermediate folders.
|
||||
enum class FolderSearchPolicy {
|
||||
// Recursive search will pass through folders whose names match the
|
||||
// pattern. Inside each one, all files will be returned. Folders with names
|
||||
// that do not match the pattern will be ignored within their interior.
|
||||
MATCH_ONLY,
|
||||
// Recursive search will pass through every folder and perform pattern
|
||||
// matching inside each one.
|
||||
ALL,
|
||||
};
|
||||
|
||||
// Determines how a FileEnumerator handles errors encountered during
|
||||
// enumeration. When no ErrorPolicy is explicitly set, FileEnumerator defaults
|
||||
// to IGNORE_ERRORS.
|
||||
enum class ErrorPolicy {
|
||||
// Errors are ignored if possible and FileEnumerator returns as many files
|
||||
// as it is able to enumerate.
|
||||
IGNORE_ERRORS,
|
||||
|
||||
// Any error encountered during enumeration will terminate the enumeration
|
||||
// immediately. An error code indicating the nature of a failure can be
|
||||
// retrieved from |GetError()|.
|
||||
STOP_ENUMERATION,
|
||||
};
|
||||
|
||||
// |root_path| is the starting directory to search for. It may or may not end
|
||||
// in a slash.
|
||||
//
|
||||
// If |recursive| is true, this will enumerate all matches in any
|
||||
// subdirectories matched as well. It does a breadth-first search, so all
|
||||
// files in one directory will be returned before any files in a
|
||||
// subdirectory.
|
||||
//
|
||||
// |file_type|, a bit mask of FileType, specifies whether the enumerator
|
||||
// should match files, directories, or both.
|
||||
//
|
||||
// |pattern| is an optional pattern for which files to match. This
|
||||
// works like shell globbing. For example, "*.txt" or "Foo???.doc".
|
||||
// However, be careful in specifying patterns that aren't cross platform
|
||||
// since the underlying code uses OS-specific matching routines. In general,
|
||||
// Windows matching is less featureful than others, so test there first.
|
||||
// If unspecified, this will match all files.
|
||||
FileEnumerator(const FilePath& root_path, bool recursive, int file_type);
|
||||
FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type,
|
||||
const FilePath::StringType& pattern);
|
||||
FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type,
|
||||
const FilePath::StringType& pattern,
|
||||
FolderSearchPolicy folder_search_policy);
|
||||
FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type,
|
||||
const FilePath::StringType& pattern,
|
||||
FolderSearchPolicy folder_search_policy,
|
||||
ErrorPolicy error_policy);
|
||||
~FileEnumerator();
|
||||
|
||||
// Returns the next file or an empty string if there are no more results.
|
||||
//
|
||||
// The returned path will incorporate the |root_path| passed in the
|
||||
// constructor: "<root_path>/file_name.txt". If the |root_path| is absolute,
|
||||
// then so will be the result of Next().
|
||||
FilePath Next();
|
||||
|
||||
// Write the file info into |info|.
|
||||
FileInfo GetInfo() const;
|
||||
|
||||
// Once |Next()| returns an empty path, enumeration has been terminated. If
|
||||
// termination was normal (i.e. no more results to enumerate) or ErrorPolicy
|
||||
// is set to IGNORE_ERRORS, this returns FILE_OK. Otherwise it returns an
|
||||
// error code reflecting why enumeration was stopped early.
|
||||
File::Error GetError() const { return error_; }
|
||||
|
||||
private:
|
||||
// Returns true if the given path should be skipped in enumeration.
|
||||
bool ShouldSkip(const FilePath& path);
|
||||
|
||||
bool IsTypeMatched(bool is_dir) const;
|
||||
|
||||
bool IsPatternMatched(const FilePath& src) const;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// True when find_data_ is valid.
|
||||
bool has_find_data_ = false;
|
||||
WIN32_FIND_DATA find_data_;
|
||||
HANDLE find_handle_ = INVALID_HANDLE_VALUE;
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// The files in the current directory
|
||||
std::vector<FileInfo> directory_entries_;
|
||||
|
||||
// Set of visited directories. Used to prevent infinite looping along
|
||||
// circular symlinks.
|
||||
std::unordered_set<ino_t> visited_directories_;
|
||||
|
||||
// The next entry to use from the directory_entries_ vector
|
||||
size_t current_directory_entry_;
|
||||
#endif
|
||||
FilePath root_path_;
|
||||
const bool recursive_;
|
||||
const int file_type_;
|
||||
FilePath::StringType pattern_;
|
||||
const FolderSearchPolicy folder_search_policy_;
|
||||
const ErrorPolicy error_policy_;
|
||||
File::Error error_ = File::FILE_OK;
|
||||
|
||||
// A stack that keeps track of which subdirectories we still need to
|
||||
// enumerate in the breadth-first search.
|
||||
base::stack<FilePath> pending_paths_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FileEnumerator);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_FILE_ENUMERATOR_H_
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_enumerator.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
void GetStat(const FilePath& path, bool show_links, stat_wrapper_t* st) {
|
||||
DCHECK(st);
|
||||
const int res = show_links ? File::Lstat(path.value().c_str(), st)
|
||||
: File::Stat(path.value().c_str(), st);
|
||||
if (res < 0) {
|
||||
// Print the stat() error message unless it was ENOENT and we're following
|
||||
// symlinks.
|
||||
if (!(errno == ENOENT && !show_links))
|
||||
DPLOG(ERROR) << "Couldn't stat" << path.value();
|
||||
memset(st, 0, sizeof(*st));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
bool ShouldShowSymLinks(int file_type) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
bool ShouldShowSymLinks(int file_type) {
|
||||
return file_type & FileEnumerator::SHOW_SYM_LINKS;
|
||||
}
|
||||
#endif // defined(OS_FUCHSIA)
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
bool ShouldTrackVisitedDirectories(int file_type) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
bool ShouldTrackVisitedDirectories(int file_type) {
|
||||
return !(file_type & FileEnumerator::SHOW_SYM_LINKS);
|
||||
}
|
||||
#endif // defined(OS_FUCHSIA)
|
||||
|
||||
} // namespace
|
||||
|
||||
// FileEnumerator::FileInfo ----------------------------------------------------
|
||||
|
||||
FileEnumerator::FileInfo::FileInfo() {
|
||||
memset(&stat_, 0, sizeof(stat_));
|
||||
}
|
||||
|
||||
bool FileEnumerator::FileInfo::IsDirectory() const {
|
||||
return S_ISDIR(stat_.st_mode);
|
||||
}
|
||||
|
||||
FilePath FileEnumerator::FileInfo::GetName() const {
|
||||
return filename_;
|
||||
}
|
||||
|
||||
int64_t FileEnumerator::FileInfo::GetSize() const {
|
||||
return stat_.st_size;
|
||||
}
|
||||
|
||||
base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
|
||||
return base::Time::FromTimeT(stat_.st_mtime);
|
||||
}
|
||||
|
||||
// FileEnumerator --------------------------------------------------------------
|
||||
|
||||
FileEnumerator::FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type)
|
||||
: FileEnumerator(root_path,
|
||||
recursive,
|
||||
file_type,
|
||||
FilePath::StringType(),
|
||||
FolderSearchPolicy::MATCH_ONLY) {}
|
||||
|
||||
FileEnumerator::FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type,
|
||||
const FilePath::StringType& pattern)
|
||||
: FileEnumerator(root_path,
|
||||
recursive,
|
||||
file_type,
|
||||
pattern,
|
||||
FolderSearchPolicy::MATCH_ONLY) {}
|
||||
|
||||
FileEnumerator::FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type,
|
||||
const FilePath::StringType& pattern,
|
||||
FolderSearchPolicy folder_search_policy)
|
||||
: FileEnumerator(root_path,
|
||||
recursive,
|
||||
file_type,
|
||||
pattern,
|
||||
folder_search_policy,
|
||||
ErrorPolicy::IGNORE_ERRORS) {}
|
||||
|
||||
FileEnumerator::FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type,
|
||||
const FilePath::StringType& pattern,
|
||||
FolderSearchPolicy folder_search_policy,
|
||||
ErrorPolicy error_policy)
|
||||
: current_directory_entry_(0),
|
||||
root_path_(root_path),
|
||||
recursive_(recursive),
|
||||
file_type_(file_type),
|
||||
pattern_(pattern),
|
||||
folder_search_policy_(folder_search_policy),
|
||||
error_policy_(error_policy) {
|
||||
// INCLUDE_DOT_DOT must not be specified if recursive.
|
||||
DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
|
||||
|
||||
if (recursive && ShouldTrackVisitedDirectories(file_type_)) {
|
||||
stat_wrapper_t st;
|
||||
GetStat(root_path, false, &st);
|
||||
visited_directories_.insert(st.st_ino);
|
||||
}
|
||||
|
||||
pending_paths_.push(root_path);
|
||||
}
|
||||
|
||||
FileEnumerator::~FileEnumerator() = default;
|
||||
|
||||
FilePath FileEnumerator::Next() {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
|
||||
++current_directory_entry_;
|
||||
|
||||
// While we've exhausted the entries in the current directory, do the next
|
||||
while (current_directory_entry_ >= directory_entries_.size()) {
|
||||
if (pending_paths_.empty())
|
||||
return FilePath();
|
||||
|
||||
root_path_ = pending_paths_.top();
|
||||
root_path_ = root_path_.StripTrailingSeparators();
|
||||
pending_paths_.pop();
|
||||
|
||||
DIR* dir = opendir(root_path_.value().c_str());
|
||||
if (!dir) {
|
||||
if (errno == 0 || error_policy_ == ErrorPolicy::IGNORE_ERRORS)
|
||||
continue;
|
||||
error_ = File::OSErrorToFileError(errno);
|
||||
return FilePath();
|
||||
}
|
||||
|
||||
directory_entries_.clear();
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
// Fuchsia does not support .. on the file system server side, see
|
||||
// https://fuchsia.googlesource.com/docs/+/master/dotdot.md and
|
||||
// https://crbug.com/735540. However, for UI purposes, having the parent
|
||||
// directory show up in directory listings makes sense, so we add it here to
|
||||
// match the expectation on other operating systems. In cases where this
|
||||
// is useful it should be resolvable locally.
|
||||
FileInfo dotdot;
|
||||
dotdot.stat_.st_mode = S_IFDIR;
|
||||
dotdot.filename_ = FilePath("..");
|
||||
if (!ShouldSkip(dotdot.filename_)) {
|
||||
directory_entries_.push_back(std::move(dotdot));
|
||||
}
|
||||
#endif // OS_FUCHSIA
|
||||
|
||||
current_directory_entry_ = 0;
|
||||
struct dirent* dent;
|
||||
// NOTE: Per the readdir() documentation, when the end of the directory is
|
||||
// reached with no errors, null is returned and errno is not changed.
|
||||
// Therefore we must reset errno to zero before calling readdir() if we
|
||||
// wish to know whether a null result indicates an error condition.
|
||||
while (errno = 0, dent = readdir(dir)) {
|
||||
FileInfo info;
|
||||
info.filename_ = FilePath(dent->d_name);
|
||||
|
||||
if (ShouldSkip(info.filename_))
|
||||
continue;
|
||||
|
||||
const bool is_pattern_matched = IsPatternMatched(info.filename_);
|
||||
|
||||
// MATCH_ONLY policy enumerates files and directories which matching
|
||||
// pattern only. So we can early skip further checks.
|
||||
if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY &&
|
||||
!is_pattern_matched)
|
||||
continue;
|
||||
|
||||
// Do not call OS stat/lstat if there is no sense to do it. If pattern is
|
||||
// not matched (file will not appear in results) and search is not
|
||||
// recursive (possible directory will not be added to pending paths) -
|
||||
// there is no sense to obtain item below.
|
||||
if (!recursive_ && !is_pattern_matched)
|
||||
continue;
|
||||
|
||||
const FilePath full_path = root_path_.Append(info.filename_);
|
||||
GetStat(full_path, ShouldShowSymLinks(file_type_), &info.stat_);
|
||||
|
||||
const bool is_dir = info.IsDirectory();
|
||||
|
||||
// Recursive mode: schedule traversal of a directory if either
|
||||
// SHOW_SYM_LINKS is on or we haven't visited the directory yet.
|
||||
if (recursive_ && is_dir &&
|
||||
(!ShouldTrackVisitedDirectories(file_type_) ||
|
||||
visited_directories_.insert(info.stat_.st_ino).second)) {
|
||||
pending_paths_.push(full_path);
|
||||
}
|
||||
|
||||
if (is_pattern_matched && IsTypeMatched(is_dir))
|
||||
directory_entries_.push_back(std::move(info));
|
||||
}
|
||||
int readdir_errno = errno;
|
||||
closedir(dir);
|
||||
if (readdir_errno != 0 && error_policy_ != ErrorPolicy::IGNORE_ERRORS) {
|
||||
error_ = File::OSErrorToFileError(readdir_errno);
|
||||
return FilePath();
|
||||
}
|
||||
|
||||
// MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern.
|
||||
// ALL policy enumerates files in all subfolders by origin pattern.
|
||||
if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY)
|
||||
pattern_.clear();
|
||||
}
|
||||
|
||||
return root_path_.Append(
|
||||
directory_entries_[current_directory_entry_].filename_);
|
||||
}
|
||||
|
||||
FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
|
||||
return directory_entries_[current_directory_entry_];
|
||||
}
|
||||
|
||||
bool FileEnumerator::IsPatternMatched(const FilePath& path) const {
|
||||
return pattern_.empty() ||
|
||||
!fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
217
TMessagesProj/jni/voip/webrtc/base/files/file_enumerator_win.cc
Normal file
217
TMessagesProj/jni/voip/webrtc/base/files/file_enumerator_win.cc
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_enumerator.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "base/win/shlwapi.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy,
|
||||
const FilePath& root_path,
|
||||
const FilePath::StringType& pattern) {
|
||||
// MATCH_ONLY policy filters incoming files by pattern on OS side. ALL policy
|
||||
// collects all files and filters them manually.
|
||||
switch (policy) {
|
||||
case FileEnumerator::FolderSearchPolicy::MATCH_ONLY:
|
||||
return root_path.Append(pattern);
|
||||
case FileEnumerator::FolderSearchPolicy::ALL:
|
||||
return root_path.Append(FILE_PATH_LITERAL("*"));
|
||||
}
|
||||
NOTREACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// FileEnumerator::FileInfo ----------------------------------------------------
|
||||
|
||||
FileEnumerator::FileInfo::FileInfo() {
|
||||
memset(&find_data_, 0, sizeof(find_data_));
|
||||
}
|
||||
|
||||
bool FileEnumerator::FileInfo::IsDirectory() const {
|
||||
return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
FilePath FileEnumerator::FileInfo::GetName() const {
|
||||
return FilePath(find_data_.cFileName);
|
||||
}
|
||||
|
||||
int64_t FileEnumerator::FileInfo::GetSize() const {
|
||||
ULARGE_INTEGER size;
|
||||
size.HighPart = find_data_.nFileSizeHigh;
|
||||
size.LowPart = find_data_.nFileSizeLow;
|
||||
DCHECK_LE(size.QuadPart,
|
||||
static_cast<ULONGLONG>(std::numeric_limits<int64_t>::max()));
|
||||
return static_cast<int64_t>(size.QuadPart);
|
||||
}
|
||||
|
||||
Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
|
||||
return Time::FromFileTime(find_data_.ftLastWriteTime);
|
||||
}
|
||||
|
||||
// FileEnumerator --------------------------------------------------------------
|
||||
|
||||
FileEnumerator::FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type)
|
||||
: FileEnumerator(root_path,
|
||||
recursive,
|
||||
file_type,
|
||||
FilePath::StringType(),
|
||||
FolderSearchPolicy::MATCH_ONLY) {}
|
||||
|
||||
FileEnumerator::FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type,
|
||||
const FilePath::StringType& pattern)
|
||||
: FileEnumerator(root_path,
|
||||
recursive,
|
||||
file_type,
|
||||
pattern,
|
||||
FolderSearchPolicy::MATCH_ONLY) {}
|
||||
|
||||
FileEnumerator::FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type,
|
||||
const FilePath::StringType& pattern,
|
||||
FolderSearchPolicy folder_search_policy)
|
||||
: FileEnumerator(root_path,
|
||||
recursive,
|
||||
file_type,
|
||||
pattern,
|
||||
folder_search_policy,
|
||||
ErrorPolicy::IGNORE_ERRORS) {}
|
||||
|
||||
FileEnumerator::FileEnumerator(const FilePath& root_path,
|
||||
bool recursive,
|
||||
int file_type,
|
||||
const FilePath::StringType& pattern,
|
||||
FolderSearchPolicy folder_search_policy,
|
||||
ErrorPolicy error_policy)
|
||||
: recursive_(recursive),
|
||||
file_type_(file_type),
|
||||
pattern_(!pattern.empty() ? pattern : FILE_PATH_LITERAL("*")),
|
||||
folder_search_policy_(folder_search_policy),
|
||||
error_policy_(error_policy) {
|
||||
// INCLUDE_DOT_DOT must not be specified if recursive.
|
||||
DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
|
||||
memset(&find_data_, 0, sizeof(find_data_));
|
||||
pending_paths_.push(root_path);
|
||||
}
|
||||
|
||||
FileEnumerator::~FileEnumerator() {
|
||||
if (find_handle_ != INVALID_HANDLE_VALUE)
|
||||
FindClose(find_handle_);
|
||||
}
|
||||
|
||||
FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
|
||||
if (!has_find_data_) {
|
||||
NOTREACHED();
|
||||
return FileInfo();
|
||||
}
|
||||
FileInfo ret;
|
||||
memcpy(&ret.find_data_, &find_data_, sizeof(find_data_));
|
||||
return ret;
|
||||
}
|
||||
|
||||
FilePath FileEnumerator::Next() {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
|
||||
while (has_find_data_ || !pending_paths_.empty()) {
|
||||
if (!has_find_data_) {
|
||||
// The last find FindFirstFile operation is done, prepare a new one.
|
||||
root_path_ = pending_paths_.top();
|
||||
pending_paths_.pop();
|
||||
|
||||
// Start a new find operation.
|
||||
const FilePath src =
|
||||
BuildSearchFilter(folder_search_policy_, root_path_, pattern_);
|
||||
find_handle_ = FindFirstFileEx(src.value().c_str(),
|
||||
FindExInfoBasic, // Omit short name.
|
||||
&find_data_, FindExSearchNameMatch,
|
||||
nullptr, FIND_FIRST_EX_LARGE_FETCH);
|
||||
has_find_data_ = true;
|
||||
} else {
|
||||
// Search for the next file/directory.
|
||||
if (!FindNextFile(find_handle_, &find_data_)) {
|
||||
FindClose(find_handle_);
|
||||
find_handle_ = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD last_error = GetLastError();
|
||||
if (INVALID_HANDLE_VALUE == find_handle_) {
|
||||
has_find_data_ = false;
|
||||
|
||||
// MATCH_ONLY policy clears pattern for matched subfolders. ALL policy
|
||||
// applies pattern for all subfolders.
|
||||
if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) {
|
||||
// This is reached when we have finished a directory and are advancing
|
||||
// to the next one in the queue. We applied the pattern (if any) to the
|
||||
// files in the root search directory, but for those directories which
|
||||
// were matched, we want to enumerate all files inside them. This will
|
||||
// happen when the handle is empty.
|
||||
pattern_ = FILE_PATH_LITERAL("*");
|
||||
}
|
||||
|
||||
if (last_error == ERROR_NO_MORE_FILES ||
|
||||
error_policy_ == ErrorPolicy::IGNORE_ERRORS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
error_ = File::OSErrorToFileError(last_error);
|
||||
return FilePath();
|
||||
}
|
||||
|
||||
const FilePath filename(find_data_.cFileName);
|
||||
if (ShouldSkip(filename))
|
||||
continue;
|
||||
|
||||
const bool is_dir =
|
||||
(find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
const FilePath abs_path = root_path_.Append(filename);
|
||||
|
||||
// Check if directory should be processed recursive.
|
||||
if (is_dir && recursive_) {
|
||||
// If |cur_file| is a directory, and we are doing recursive searching,
|
||||
// add it to pending_paths_ so we scan it after we finish scanning this
|
||||
// directory. However, don't do recursion through reparse points or we
|
||||
// may end up with an infinite cycle.
|
||||
DWORD attributes = GetFileAttributes(abs_path.value().c_str());
|
||||
if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT))
|
||||
pending_paths_.push(abs_path);
|
||||
}
|
||||
|
||||
if (IsTypeMatched(is_dir) && IsPatternMatched(filename))
|
||||
return abs_path;
|
||||
}
|
||||
return FilePath();
|
||||
}
|
||||
|
||||
bool FileEnumerator::IsPatternMatched(const FilePath& src) const {
|
||||
switch (folder_search_policy_) {
|
||||
case FolderSearchPolicy::MATCH_ONLY:
|
||||
// MATCH_ONLY policy filters by pattern on search request, so all found
|
||||
// files already fits to pattern.
|
||||
return true;
|
||||
case FolderSearchPolicy::ALL:
|
||||
// ALL policy enumerates all files, we need to check pattern match
|
||||
// manually.
|
||||
return PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE;
|
||||
}
|
||||
NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
1382
TMessagesProj/jni/voip/webrtc/base/files/file_path.cc
Normal file
1382
TMessagesProj/jni/voip/webrtc/base/files/file_path.cc
Normal file
File diff suppressed because it is too large
Load diff
484
TMessagesProj/jni/voip/webrtc/base/files/file_path.h
Normal file
484
TMessagesProj/jni/voip/webrtc/base/files/file_path.h
Normal file
|
|
@ -0,0 +1,484 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// FilePath is a container for pathnames stored in a platform's native string
|
||||
// type, providing containers for manipulation in according with the
|
||||
// platform's conventions for pathnames. It supports the following path
|
||||
// types:
|
||||
//
|
||||
// POSIX Windows
|
||||
// --------------- ----------------------------------
|
||||
// Fundamental type char[] wchar_t[]
|
||||
// Encoding unspecified* UTF-16
|
||||
// Separator / \, tolerant of /
|
||||
// Drive letters no case-insensitive A-Z followed by :
|
||||
// Alternate root // (surprise!) \\, for UNC paths
|
||||
//
|
||||
// * The encoding need not be specified on POSIX systems, although some
|
||||
// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8.
|
||||
// Chrome OS also uses UTF-8.
|
||||
// Linux does not specify an encoding, but in practice, the locale's
|
||||
// character set may be used.
|
||||
//
|
||||
// For more arcane bits of path trivia, see below.
|
||||
//
|
||||
// FilePath objects are intended to be used anywhere paths are. An
|
||||
// application may pass FilePath objects around internally, masking the
|
||||
// underlying differences between systems, only differing in implementation
|
||||
// where interfacing directly with the system. For example, a single
|
||||
// OpenFile(const FilePath &) function may be made available, allowing all
|
||||
// callers to operate without regard to the underlying implementation. On
|
||||
// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might
|
||||
// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This
|
||||
// allows each platform to pass pathnames around without requiring conversions
|
||||
// between encodings, which has an impact on performance, but more imporantly,
|
||||
// has an impact on correctness on platforms that do not have well-defined
|
||||
// encodings for pathnames.
|
||||
//
|
||||
// Several methods are available to perform common operations on a FilePath
|
||||
// object, such as determining the parent directory (DirName), isolating the
|
||||
// final path component (BaseName), and appending a relative pathname string
|
||||
// to an existing FilePath object (Append). These methods are highly
|
||||
// recommended over attempting to split and concatenate strings directly.
|
||||
// These methods are based purely on string manipulation and knowledge of
|
||||
// platform-specific pathname conventions, and do not consult the filesystem
|
||||
// at all, making them safe to use without fear of blocking on I/O operations.
|
||||
// These methods do not function as mutators but instead return distinct
|
||||
// instances of FilePath objects, and are therefore safe to use on const
|
||||
// objects. The objects themselves are safe to share between threads.
|
||||
//
|
||||
// To aid in initialization of FilePath objects from string literals, a
|
||||
// FILE_PATH_LITERAL macro is provided, which accounts for the difference
|
||||
// between char[]-based pathnames on POSIX systems and wchar_t[]-based
|
||||
// pathnames on Windows.
|
||||
//
|
||||
// As a precaution against premature truncation, paths can't contain NULs.
|
||||
//
|
||||
// Because a FilePath object should not be instantiated at the global scope,
|
||||
// instead, use a FilePath::CharType[] and initialize it with
|
||||
// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the
|
||||
// character array. Example:
|
||||
//
|
||||
// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt");
|
||||
// |
|
||||
// | void Function() {
|
||||
// | FilePath log_file_path(kLogFileName);
|
||||
// | [...]
|
||||
// | }
|
||||
//
|
||||
// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even
|
||||
// when the UI language is RTL. This means you always need to pass filepaths
|
||||
// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the
|
||||
// RTL UI.
|
||||
//
|
||||
// This is a very common source of bugs, please try to keep this in mind.
|
||||
//
|
||||
// ARCANE BITS OF PATH TRIVIA
|
||||
//
|
||||
// - A double leading slash is actually part of the POSIX standard. Systems
|
||||
// are allowed to treat // as an alternate root, as Windows does for UNC
|
||||
// (network share) paths. Most POSIX systems don't do anything special
|
||||
// with two leading slashes, but FilePath handles this case properly
|
||||
// in case it ever comes across such a system. FilePath needs this support
|
||||
// for Windows UNC paths, anyway.
|
||||
// References:
|
||||
// The Open Group Base Specifications Issue 7, sections 3.267 ("Pathname")
|
||||
// and 4.12 ("Pathname Resolution"), available at:
|
||||
// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267
|
||||
// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12
|
||||
//
|
||||
// - Windows treats c:\\ the same way it treats \\. This was intended to
|
||||
// allow older applications that require drive letters to support UNC paths
|
||||
// like \\server\share\path, by permitting c:\\server\share\path as an
|
||||
// equivalent. Since the OS treats these paths specially, FilePath needs
|
||||
// to do the same. Since Windows can use either / or \ as the separator,
|
||||
// FilePath treats c://, c:\\, //, and \\ all equivalently.
|
||||
// Reference:
|
||||
// The Old New Thing, "Why is a drive letter permitted in front of UNC
|
||||
// paths (sometimes)?", available at:
|
||||
// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx
|
||||
|
||||
#ifndef BASE_FILES_FILE_PATH_H_
|
||||
#define BASE_FILES_FILE_PATH_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
// Windows-style drive letter support and pathname separator characters can be
|
||||
// enabled and disabled independently, to aid testing. These #defines are
|
||||
// here so that the same setting can be used in both the implementation and
|
||||
// in the unit test.
|
||||
#if defined(OS_WIN)
|
||||
#define FILE_PATH_USES_DRIVE_LETTERS
|
||||
#define FILE_PATH_USES_WIN_SEPARATORS
|
||||
#endif // OS_WIN
|
||||
|
||||
// To print path names portably use PRFilePath (based on PRIuS and friends from
|
||||
// C99 and format_macros.h) like this:
|
||||
// base::StringPrintf("Path is %" PRFilePath ".\n", path.value().c_str());
|
||||
#if defined(OS_WIN)
|
||||
#define PRFilePath "ls"
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
#define PRFilePath "s"
|
||||
#endif // OS_WIN
|
||||
|
||||
// Macros for string literal initialization of FilePath::CharType[].
|
||||
#if defined(OS_WIN)
|
||||
#define FILE_PATH_LITERAL(x) L##x
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
#define FILE_PATH_LITERAL(x) x
|
||||
#endif // OS_WIN
|
||||
|
||||
namespace base {
|
||||
|
||||
class Pickle;
|
||||
class PickleIterator;
|
||||
|
||||
// An abstraction to isolate users from the differences between native
|
||||
// pathnames on different platforms.
|
||||
class BASE_EXPORT FilePath {
|
||||
public:
|
||||
#if defined(OS_WIN)
|
||||
// On Windows, for Unicode-aware applications, native pathnames are wchar_t
|
||||
// arrays encoded in UTF-16.
|
||||
typedef std::wstring StringType;
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// On most platforms, native pathnames are char arrays, and the encoding
|
||||
// may or may not be specified. On Mac OS X, native pathnames are encoded
|
||||
// in UTF-8.
|
||||
typedef std::string StringType;
|
||||
#endif // OS_WIN
|
||||
|
||||
typedef BasicStringPiece<StringType> StringPieceType;
|
||||
typedef StringType::value_type CharType;
|
||||
|
||||
// Null-terminated array of separators used to separate components in
|
||||
// hierarchical paths. Each character in this array is a valid separator,
|
||||
// but kSeparators[0] is treated as the canonical separator and will be used
|
||||
// when composing pathnames.
|
||||
static const CharType kSeparators[];
|
||||
|
||||
// base::size(kSeparators).
|
||||
static const size_t kSeparatorsLength;
|
||||
|
||||
// A special path component meaning "this directory."
|
||||
static const CharType kCurrentDirectory[];
|
||||
|
||||
// A special path component meaning "the parent directory."
|
||||
static const CharType kParentDirectory[];
|
||||
|
||||
// The character used to identify a file extension.
|
||||
static const CharType kExtensionSeparator;
|
||||
|
||||
FilePath();
|
||||
FilePath(const FilePath& that);
|
||||
explicit FilePath(StringPieceType path);
|
||||
~FilePath();
|
||||
FilePath& operator=(const FilePath& that);
|
||||
|
||||
// Constructs FilePath with the contents of |that|, which is left in valid but
|
||||
// unspecified state.
|
||||
FilePath(FilePath&& that) noexcept;
|
||||
// Replaces the contents with those of |that|, which is left in valid but
|
||||
// unspecified state.
|
||||
FilePath& operator=(FilePath&& that);
|
||||
|
||||
bool operator==(const FilePath& that) const;
|
||||
|
||||
bool operator!=(const FilePath& that) const;
|
||||
|
||||
// Required for some STL containers and operations
|
||||
bool operator<(const FilePath& that) const {
|
||||
return path_ < that.path_;
|
||||
}
|
||||
|
||||
const StringType& value() const { return path_; }
|
||||
|
||||
bool empty() const { return path_.empty(); }
|
||||
|
||||
void clear() { path_.clear(); }
|
||||
|
||||
// Returns true if |character| is in kSeparators.
|
||||
static bool IsSeparator(CharType character);
|
||||
|
||||
// Returns a vector of all of the components of the provided path. It is
|
||||
// equivalent to calling DirName().value() on the path's root component,
|
||||
// and BaseName().value() on each child component.
|
||||
//
|
||||
// To make sure this is lossless so we can differentiate absolute and
|
||||
// relative paths, the root slash will be included even though no other
|
||||
// slashes will be. The precise behavior is:
|
||||
//
|
||||
// Posix: "/foo/bar" -> [ "/", "foo", "bar" ]
|
||||
// Windows: "C:\foo\bar" -> [ "C:", "\\", "foo", "bar" ]
|
||||
void GetComponents(std::vector<FilePath::StringType>* components) const;
|
||||
|
||||
// Returns true if this FilePath is a parent or ancestor of the |child|.
|
||||
// Absolute and relative paths are accepted i.e. /foo is a parent to /foo/bar,
|
||||
// and foo is a parent to foo/bar. Any ancestor is considered a parent i.e. /a
|
||||
// is a parent to both /a/b and /a/b/c. Does not convert paths to absolute,
|
||||
// follow symlinks or directory navigation (e.g. ".."). A path is *NOT* its
|
||||
// own parent.
|
||||
bool IsParent(const FilePath& child) const;
|
||||
|
||||
// If IsParent(child) holds, appends to path (if non-NULL) the
|
||||
// relative path to child and returns true. For example, if parent
|
||||
// holds "/Users/johndoe/Library/Application Support", child holds
|
||||
// "/Users/johndoe/Library/Application Support/Google/Chrome/Default", and
|
||||
// *path holds "/Users/johndoe/Library/Caches", then after
|
||||
// parent.AppendRelativePath(child, path) is called *path will hold
|
||||
// "/Users/johndoe/Library/Caches/Google/Chrome/Default". Otherwise,
|
||||
// returns false.
|
||||
bool AppendRelativePath(const FilePath& child, FilePath* path) const;
|
||||
|
||||
// Returns a FilePath corresponding to the directory containing the path
|
||||
// named by this object, stripping away the file component. If this object
|
||||
// only contains one component, returns a FilePath identifying
|
||||
// kCurrentDirectory. If this object already refers to the root directory,
|
||||
// returns a FilePath identifying the root directory. Please note that this
|
||||
// doesn't resolve directory navigation, e.g. the result for "../a" is "..".
|
||||
FilePath DirName() const WARN_UNUSED_RESULT;
|
||||
|
||||
// Returns a FilePath corresponding to the last path component of this
|
||||
// object, either a file or a directory. If this object already refers to
|
||||
// the root directory, returns a FilePath identifying the root directory;
|
||||
// this is the only situation in which BaseName will return an absolute path.
|
||||
FilePath BaseName() const WARN_UNUSED_RESULT;
|
||||
|
||||
// Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if
|
||||
// the file has no extension. If non-empty, Extension() will always start
|
||||
// with precisely one ".". The following code should always work regardless
|
||||
// of the value of path. For common double-extensions like .tar.gz and
|
||||
// .user.js, this method returns the combined extension. For a single
|
||||
// component, use FinalExtension().
|
||||
// new_path = path.RemoveExtension().value().append(path.Extension());
|
||||
// ASSERT(new_path == path.value());
|
||||
// NOTE: this is different from the original file_util implementation which
|
||||
// returned the extension without a leading "." ("jpg" instead of ".jpg")
|
||||
StringType Extension() const WARN_UNUSED_RESULT;
|
||||
|
||||
// Returns the path's file extension, as in Extension(), but will
|
||||
// never return a double extension.
|
||||
//
|
||||
// TODO(davidben): Check all our extension-sensitive code to see if
|
||||
// we can rename this to Extension() and the other to something like
|
||||
// LongExtension(), defaulting to short extensions and leaving the
|
||||
// long "extensions" to logic like base::GetUniquePathNumber().
|
||||
StringType FinalExtension() const WARN_UNUSED_RESULT;
|
||||
|
||||
// Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg"
|
||||
// NOTE: this is slightly different from the similar file_util implementation
|
||||
// which returned simply 'jojo'.
|
||||
FilePath RemoveExtension() const WARN_UNUSED_RESULT;
|
||||
|
||||
// Removes the path's file extension, as in RemoveExtension(), but
|
||||
// ignores double extensions.
|
||||
FilePath RemoveFinalExtension() const WARN_UNUSED_RESULT;
|
||||
|
||||
// Inserts |suffix| after the file name portion of |path| but before the
|
||||
// extension. Returns "" if BaseName() == "." or "..".
|
||||
// Examples:
|
||||
// path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg"
|
||||
// path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg"
|
||||
// path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)"
|
||||
// path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)"
|
||||
FilePath InsertBeforeExtension(
|
||||
StringPieceType suffix) const WARN_UNUSED_RESULT;
|
||||
FilePath InsertBeforeExtensionASCII(
|
||||
StringPiece suffix) const WARN_UNUSED_RESULT;
|
||||
|
||||
// Adds |extension| to |file_name|. Returns the current FilePath if
|
||||
// |extension| is empty. Returns "" if BaseName() == "." or "..".
|
||||
FilePath AddExtension(StringPieceType extension) const WARN_UNUSED_RESULT;
|
||||
|
||||
// Like above, but takes the extension as an ASCII string. See AppendASCII for
|
||||
// details on how this is handled.
|
||||
FilePath AddExtensionASCII(StringPiece extension) const WARN_UNUSED_RESULT;
|
||||
|
||||
// Replaces the extension of |file_name| with |extension|. If |file_name|
|
||||
// does not have an extension, then |extension| is added. If |extension| is
|
||||
// empty, then the extension is removed from |file_name|.
|
||||
// Returns "" if BaseName() == "." or "..".
|
||||
FilePath ReplaceExtension(StringPieceType extension) const WARN_UNUSED_RESULT;
|
||||
|
||||
// Returns true if the file path matches the specified extension. The test is
|
||||
// case insensitive. Don't forget the leading period if appropriate.
|
||||
bool MatchesExtension(StringPieceType extension) const;
|
||||
|
||||
// Returns a FilePath by appending a separator and the supplied path
|
||||
// component to this object's path. Append takes care to avoid adding
|
||||
// excessive separators if this object's path already ends with a separator.
|
||||
// If this object's path is kCurrentDirectory, a new FilePath corresponding
|
||||
// only to |component| is returned. |component| must be a relative path;
|
||||
// it is an error to pass an absolute path.
|
||||
FilePath Append(StringPieceType component) const WARN_UNUSED_RESULT;
|
||||
FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT;
|
||||
|
||||
// Although Windows StringType is std::wstring, since the encoding it uses for
|
||||
// paths is well defined, it can handle ASCII path components as well.
|
||||
// Mac uses UTF8, and since ASCII is a subset of that, it works there as well.
|
||||
// On Linux, although it can use any 8-bit encoding for paths, we assume that
|
||||
// ASCII is a valid subset, regardless of the encoding, since many operating
|
||||
// system paths will always be ASCII.
|
||||
FilePath AppendASCII(StringPiece component) const WARN_UNUSED_RESULT;
|
||||
|
||||
// Returns true if this FilePath contains an absolute path. On Windows, an
|
||||
// absolute path begins with either a drive letter specification followed by
|
||||
// a separator character, or with two separator characters. On POSIX
|
||||
// platforms, an absolute path begins with a separator character.
|
||||
bool IsAbsolute() const;
|
||||
|
||||
// Returns true if the patch ends with a path separator character.
|
||||
bool EndsWithSeparator() const WARN_UNUSED_RESULT;
|
||||
|
||||
// Returns a copy of this FilePath that ends with a trailing separator. If
|
||||
// the input path is empty, an empty FilePath will be returned.
|
||||
FilePath AsEndingWithSeparator() const WARN_UNUSED_RESULT;
|
||||
|
||||
// Returns a copy of this FilePath that does not end with a trailing
|
||||
// separator.
|
||||
FilePath StripTrailingSeparators() const WARN_UNUSED_RESULT;
|
||||
|
||||
// Returns true if this FilePath contains an attempt to reference a parent
|
||||
// directory (e.g. has a path component that is "..").
|
||||
bool ReferencesParent() const;
|
||||
|
||||
// Return a Unicode human-readable version of this path.
|
||||
// Warning: you can *not*, in general, go from a display name back to a real
|
||||
// path. Only use this when displaying paths to users, not just when you
|
||||
// want to stuff a string16 into some other API.
|
||||
string16 LossyDisplayName() const;
|
||||
|
||||
// Return the path as ASCII, or the empty string if the path is not ASCII.
|
||||
// This should only be used for cases where the FilePath is representing a
|
||||
// known-ASCII filename.
|
||||
std::string MaybeAsASCII() const;
|
||||
|
||||
// Return the path as UTF-8.
|
||||
//
|
||||
// This function is *unsafe* as there is no way to tell what encoding is
|
||||
// used in file names on POSIX systems other than Mac and Chrome OS,
|
||||
// although UTF-8 is practically used everywhere these days. To mitigate
|
||||
// the encoding issue, this function internally calls
|
||||
// SysNativeMBToWide() on POSIX systems other than Mac and Chrome OS,
|
||||
// per assumption that the current locale's encoding is used in file
|
||||
// names, but this isn't a perfect solution.
|
||||
//
|
||||
// Once it becomes safe to to stop caring about non-UTF-8 file names,
|
||||
// the SysNativeMBToWide() hack will be removed from the code, along
|
||||
// with "Unsafe" in the function name.
|
||||
std::string AsUTF8Unsafe() const;
|
||||
|
||||
// Similar to AsUTF8Unsafe, but returns UTF-16 instead.
|
||||
string16 AsUTF16Unsafe() const;
|
||||
|
||||
// Returns a FilePath object from a path name in UTF-8. This function
|
||||
// should only be used for cases where you are sure that the input
|
||||
// string is UTF-8.
|
||||
//
|
||||
// Like AsUTF8Unsafe(), this function is unsafe. This function
|
||||
// internally calls SysWideToNativeMB() on POSIX systems other than Mac
|
||||
// and Chrome OS, to mitigate the encoding issue. See the comment at
|
||||
// AsUTF8Unsafe() for details.
|
||||
static FilePath FromUTF8Unsafe(StringPiece utf8);
|
||||
|
||||
// Similar to FromUTF8Unsafe, but accepts UTF-16 instead.
|
||||
static FilePath FromUTF16Unsafe(StringPiece16 utf16);
|
||||
|
||||
void WriteToPickle(Pickle* pickle) const;
|
||||
bool ReadFromPickle(PickleIterator* iter);
|
||||
|
||||
// Normalize all path separators to backslash on Windows
|
||||
// (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
|
||||
FilePath NormalizePathSeparators() const;
|
||||
|
||||
// Normalize all path separattors to given type on Windows
|
||||
// (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
|
||||
FilePath NormalizePathSeparatorsTo(CharType separator) const;
|
||||
|
||||
// Compare two strings in the same way the file system does.
|
||||
// Note that these always ignore case, even on file systems that are case-
|
||||
// sensitive. If case-sensitive comparison is ever needed, add corresponding
|
||||
// methods here.
|
||||
// The methods are written as a static method so that they can also be used
|
||||
// on parts of a file path, e.g., just the extension.
|
||||
// CompareIgnoreCase() returns -1, 0 or 1 for less-than, equal-to and
|
||||
// greater-than respectively.
|
||||
static int CompareIgnoreCase(StringPieceType string1,
|
||||
StringPieceType string2);
|
||||
static bool CompareEqualIgnoreCase(StringPieceType string1,
|
||||
StringPieceType string2) {
|
||||
return CompareIgnoreCase(string1, string2) == 0;
|
||||
}
|
||||
static bool CompareLessIgnoreCase(StringPieceType string1,
|
||||
StringPieceType string2) {
|
||||
return CompareIgnoreCase(string1, string2) < 0;
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// Returns the string in the special canonical decomposed form as defined for
|
||||
// HFS, which is close to, but not quite, decomposition form D. See
|
||||
// http://developer.apple.com/mac/library/technotes/tn/tn1150.html#UnicodeSubtleties
|
||||
// for further comments.
|
||||
// Returns the epmty string if the conversion failed.
|
||||
static StringType GetHFSDecomposedForm(StringPieceType string);
|
||||
|
||||
// Special UTF-8 version of FastUnicodeCompare. Cf:
|
||||
// http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm
|
||||
// IMPORTANT: The input strings must be in the special HFS decomposed form!
|
||||
// (cf. above GetHFSDecomposedForm method)
|
||||
static int HFSFastUnicodeCompare(StringPieceType string1,
|
||||
StringPieceType string2);
|
||||
#endif
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
// On android, file selection dialog can return a file with content uri
|
||||
// scheme(starting with content://). Content uri needs to be opened with
|
||||
// ContentResolver to guarantee that the app has appropriate permissions
|
||||
// to access it.
|
||||
// Returns true if the path is a content uri, or false otherwise.
|
||||
bool IsContentUri() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Remove trailing separators from this object. If the path is absolute, it
|
||||
// will never be stripped any more than to refer to the absolute root
|
||||
// directory, so "////" will become "/", not "". A leading pair of
|
||||
// separators is never stripped, to support alternate roots. This is used to
|
||||
// support UNC paths on Windows.
|
||||
void StripTrailingSeparatorsInternal();
|
||||
|
||||
StringType path_;
|
||||
};
|
||||
|
||||
BASE_EXPORT std::ostream& operator<<(std::ostream& out,
|
||||
const FilePath& file_path);
|
||||
|
||||
} // namespace base
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<base::FilePath> {
|
||||
typedef base::FilePath argument_type;
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(argument_type const& f) const {
|
||||
return hash<base::FilePath::StringType>()(f.value());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // BASE_FILES_FILE_PATH_H_
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/stl_util.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
|
||||
const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/");
|
||||
#else // FILE_PATH_USES_WIN_SEPARATORS
|
||||
const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
|
||||
#endif // FILE_PATH_USES_WIN_SEPARATORS
|
||||
|
||||
const size_t FilePath::kSeparatorsLength = base::size(kSeparators);
|
||||
|
||||
const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
|
||||
const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
|
||||
|
||||
const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.');
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Cross platform methods for FilePathWatcher. See the various platform
|
||||
// specific implementation files, too.
|
||||
|
||||
#include "base/files/file_path_watcher.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
FilePathWatcher::~FilePathWatcher() {
|
||||
DCHECK(sequence_checker_.CalledOnValidSequence());
|
||||
impl_->Cancel();
|
||||
}
|
||||
|
||||
// static
|
||||
bool FilePathWatcher::RecursiveWatchAvailable() {
|
||||
#if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) || \
|
||||
defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
|
||||
return true;
|
||||
#else
|
||||
// FSEvents isn't available on iOS.
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
FilePathWatcher::PlatformDelegate::PlatformDelegate(): cancelled_(false) {
|
||||
}
|
||||
|
||||
FilePathWatcher::PlatformDelegate::~PlatformDelegate() {
|
||||
DCHECK(is_cancelled());
|
||||
}
|
||||
|
||||
bool FilePathWatcher::Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const Callback& callback) {
|
||||
DCHECK(sequence_checker_.CalledOnValidSequence());
|
||||
DCHECK(path.IsAbsolute());
|
||||
return impl_->Watch(path, recursive, callback);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
111
TMessagesProj/jni/voip/webrtc/base/files/file_path_watcher.h
Normal file
111
TMessagesProj/jni/voip/webrtc/base/files/file_path_watcher.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This module provides a way to monitor a file or directory for changes.
|
||||
|
||||
#ifndef BASE_FILES_FILE_PATH_WATCHER_H_
|
||||
#define BASE_FILES_FILE_PATH_WATCHER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "base/sequenced_task_runner.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// This class lets you register interest in changes on a FilePath.
|
||||
// The callback will get called whenever the file or directory referenced by the
|
||||
// FilePath is changed, including created or deleted. Due to limitations in the
|
||||
// underlying OS APIs, FilePathWatcher has slightly different semantics on OS X
|
||||
// than on Windows or Linux. FilePathWatcher on Linux and Windows will detect
|
||||
// modifications to files in a watched directory. FilePathWatcher on Mac will
|
||||
// detect the creation and deletion of files in a watched directory, but will
|
||||
// not detect modifications to those files. See file_path_watcher_kqueue.cc for
|
||||
// details.
|
||||
//
|
||||
// Must be destroyed on the sequence that invokes Watch().
|
||||
class BASE_EXPORT FilePathWatcher {
|
||||
public:
|
||||
// Callback type for Watch(). |path| points to the file that was updated,
|
||||
// and |error| is true if the platform specific code detected an error. In
|
||||
// that case, the callback won't be invoked again.
|
||||
using Callback =
|
||||
base::RepeatingCallback<void(const FilePath& path, bool error)>;
|
||||
|
||||
// Used internally to encapsulate different members on different platforms.
|
||||
class PlatformDelegate {
|
||||
public:
|
||||
PlatformDelegate();
|
||||
virtual ~PlatformDelegate();
|
||||
|
||||
// Start watching for the given |path| and notify |delegate| about changes.
|
||||
virtual bool Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const Callback& callback) WARN_UNUSED_RESULT = 0;
|
||||
|
||||
// Stop watching. This is called from FilePathWatcher's dtor in order to
|
||||
// allow to shut down properly while the object is still alive.
|
||||
virtual void Cancel() = 0;
|
||||
|
||||
protected:
|
||||
friend class FilePathWatcher;
|
||||
|
||||
scoped_refptr<SequencedTaskRunner> task_runner() const {
|
||||
return task_runner_;
|
||||
}
|
||||
|
||||
void set_task_runner(scoped_refptr<SequencedTaskRunner> runner) {
|
||||
task_runner_ = std::move(runner);
|
||||
}
|
||||
|
||||
// Must be called before the PlatformDelegate is deleted.
|
||||
void set_cancelled() {
|
||||
cancelled_ = true;
|
||||
}
|
||||
|
||||
bool is_cancelled() const {
|
||||
return cancelled_;
|
||||
}
|
||||
|
||||
private:
|
||||
scoped_refptr<SequencedTaskRunner> task_runner_;
|
||||
bool cancelled_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PlatformDelegate);
|
||||
};
|
||||
|
||||
FilePathWatcher();
|
||||
~FilePathWatcher();
|
||||
|
||||
// Returns true if the platform and OS version support recursive watches.
|
||||
static bool RecursiveWatchAvailable();
|
||||
|
||||
// Invokes |callback| whenever updates to |path| are detected. This should be
|
||||
// called at most once. Set |recursive| to true to watch |path| and its
|
||||
// children. The callback will be invoked on the same sequence. Returns true
|
||||
// on success.
|
||||
//
|
||||
// On POSIX, this must be called from a thread that supports
|
||||
// FileDescriptorWatcher.
|
||||
//
|
||||
// Recursive watch is not supported on all platforms and file systems.
|
||||
// Watch() will return false in the case of failure.
|
||||
bool Watch(const FilePath& path, bool recursive, const Callback& callback);
|
||||
|
||||
private:
|
||||
std::unique_ptr<PlatformDelegate> impl_;
|
||||
|
||||
SequenceChecker sequence_checker_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FilePathWatcher);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_FILE_PATH_WATCHER_H_
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_path_watcher_fsevents.h"
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
// The latency parameter passed to FSEventsStreamCreate().
|
||||
const CFAbsoluteTime kEventLatencySeconds = 0.3;
|
||||
|
||||
// Resolve any symlinks in the path.
|
||||
FilePath ResolvePath(const FilePath& path) {
|
||||
const unsigned kMaxLinksToResolve = 255;
|
||||
|
||||
std::vector<FilePath::StringType> component_vector;
|
||||
path.GetComponents(&component_vector);
|
||||
std::list<FilePath::StringType>
|
||||
components(component_vector.begin(), component_vector.end());
|
||||
|
||||
FilePath result;
|
||||
unsigned resolve_count = 0;
|
||||
while (resolve_count < kMaxLinksToResolve && !components.empty()) {
|
||||
FilePath component(*components.begin());
|
||||
components.pop_front();
|
||||
|
||||
FilePath current;
|
||||
if (component.IsAbsolute()) {
|
||||
current = component;
|
||||
} else {
|
||||
current = result.Append(component);
|
||||
}
|
||||
|
||||
FilePath target;
|
||||
if (ReadSymbolicLink(current, &target)) {
|
||||
if (target.IsAbsolute())
|
||||
result.clear();
|
||||
std::vector<FilePath::StringType> target_components;
|
||||
target.GetComponents(&target_components);
|
||||
components.insert(components.begin(), target_components.begin(),
|
||||
target_components.end());
|
||||
resolve_count++;
|
||||
} else {
|
||||
result = current;
|
||||
}
|
||||
}
|
||||
|
||||
if (resolve_count >= kMaxLinksToResolve)
|
||||
result.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FilePathWatcherFSEvents::FilePathWatcherFSEvents()
|
||||
: queue_(dispatch_queue_create(
|
||||
base::StringPrintf("org.chromium.base.FilePathWatcher.%p", this)
|
||||
.c_str(),
|
||||
DISPATCH_QUEUE_SERIAL)),
|
||||
fsevent_stream_(nullptr),
|
||||
weak_factory_(this) {}
|
||||
|
||||
FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
|
||||
DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence());
|
||||
DCHECK(callback_.is_null())
|
||||
<< "Cancel() must be called before FilePathWatcher is destroyed.";
|
||||
}
|
||||
|
||||
bool FilePathWatcherFSEvents::Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) {
|
||||
DCHECK(!callback.is_null());
|
||||
DCHECK(callback_.is_null());
|
||||
|
||||
// This class could support non-recursive watches, but that is currently
|
||||
// left to FilePathWatcherKQueue.
|
||||
if (!recursive)
|
||||
return false;
|
||||
|
||||
set_task_runner(SequencedTaskRunnerHandle::Get());
|
||||
callback_ = callback;
|
||||
|
||||
FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
|
||||
// The block runtime would implicitly capture the reference, not the object
|
||||
// it's referencing. Copy the path into a local, so that the value is
|
||||
// captured by the block's scope.
|
||||
const FilePath path_copy(path);
|
||||
|
||||
dispatch_async(queue_, ^{
|
||||
StartEventStream(start_event, path_copy);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilePathWatcherFSEvents::Cancel() {
|
||||
set_cancelled();
|
||||
callback_.Reset();
|
||||
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
// Switch to the dispatch queue to tear down the event stream. As the queue is
|
||||
// owned by |this|, and this method is called from the destructor, execute the
|
||||
// block synchronously.
|
||||
dispatch_sync(queue_, ^{
|
||||
if (fsevent_stream_) {
|
||||
DestroyEventStream();
|
||||
target_.clear();
|
||||
resolved_target_.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// static
|
||||
void FilePathWatcherFSEvents::FSEventsCallback(
|
||||
ConstFSEventStreamRef stream,
|
||||
void* event_watcher,
|
||||
size_t num_events,
|
||||
void* event_paths,
|
||||
const FSEventStreamEventFlags flags[],
|
||||
const FSEventStreamEventId event_ids[]) {
|
||||
FilePathWatcherFSEvents* watcher =
|
||||
reinterpret_cast<FilePathWatcherFSEvents*>(event_watcher);
|
||||
bool root_changed = watcher->ResolveTargetPath();
|
||||
std::vector<FilePath> paths;
|
||||
FSEventStreamEventId root_change_at = FSEventStreamGetLatestEventId(stream);
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
if (flags[i] & kFSEventStreamEventFlagRootChanged)
|
||||
root_changed = true;
|
||||
if (event_ids[i])
|
||||
root_change_at = std::min(root_change_at, event_ids[i]);
|
||||
paths.push_back(FilePath(
|
||||
reinterpret_cast<char**>(event_paths)[i]).StripTrailingSeparators());
|
||||
}
|
||||
|
||||
// Reinitialize the event stream if we find changes to the root. This is
|
||||
// necessary since FSEvents doesn't report any events for the subtree after
|
||||
// the directory to be watched gets created.
|
||||
if (root_changed) {
|
||||
// Resetting the event stream from within the callback fails (FSEvents spews
|
||||
// bad file descriptor errors), so do the reset asynchronously.
|
||||
//
|
||||
// We can't dispatch_async a call to UpdateEventStream() directly because
|
||||
// there would be no guarantee that |watcher| still exists when it runs.
|
||||
//
|
||||
// Instead, bounce on task_runner() and use a WeakPtr to verify that
|
||||
// |watcher| still exists. If it does, dispatch_async a call to
|
||||
// UpdateEventStream(). Because the destructor of |watcher| runs on
|
||||
// task_runner() and calls dispatch_sync, it is guaranteed that |watcher|
|
||||
// still exists when UpdateEventStream() runs.
|
||||
watcher->task_runner()->PostTask(
|
||||
FROM_HERE, BindOnce(
|
||||
[](WeakPtr<FilePathWatcherFSEvents> weak_watcher,
|
||||
FSEventStreamEventId root_change_at) {
|
||||
if (!weak_watcher)
|
||||
return;
|
||||
FilePathWatcherFSEvents* watcher = weak_watcher.get();
|
||||
dispatch_async(watcher->queue_, ^{
|
||||
watcher->UpdateEventStream(root_change_at);
|
||||
});
|
||||
},
|
||||
watcher->weak_factory_.GetWeakPtr(), root_change_at));
|
||||
}
|
||||
|
||||
watcher->OnFilePathsChanged(paths);
|
||||
}
|
||||
|
||||
void FilePathWatcherFSEvents::OnFilePathsChanged(
|
||||
const std::vector<FilePath>& paths) {
|
||||
DCHECK(!resolved_target_.empty());
|
||||
task_runner()->PostTask(
|
||||
FROM_HERE,
|
||||
BindOnce(&FilePathWatcherFSEvents::DispatchEvents,
|
||||
weak_factory_.GetWeakPtr(), paths, target_, resolved_target_));
|
||||
}
|
||||
|
||||
void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths,
|
||||
const FilePath& target,
|
||||
const FilePath& resolved_target) {
|
||||
DCHECK(task_runner()->RunsTasksInCurrentSequence());
|
||||
|
||||
// Don't issue callbacks after Cancel() has been called.
|
||||
if (is_cancelled() || callback_.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const FilePath& path : paths) {
|
||||
if (resolved_target.IsParent(path) || resolved_target == path) {
|
||||
callback_.Run(target, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FilePathWatcherFSEvents::UpdateEventStream(
|
||||
FSEventStreamEventId start_event) {
|
||||
// It can happen that the watcher gets canceled while tasks that call this
|
||||
// function are still in flight, so abort if this situation is detected.
|
||||
if (resolved_target_.empty())
|
||||
return;
|
||||
|
||||
if (fsevent_stream_)
|
||||
DestroyEventStream();
|
||||
|
||||
ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString(
|
||||
NULL, resolved_target_.value().c_str(), kCFStringEncodingMacHFS));
|
||||
ScopedCFTypeRef<CFStringRef> cf_dir_path(CFStringCreateWithCString(
|
||||
NULL, resolved_target_.DirName().value().c_str(),
|
||||
kCFStringEncodingMacHFS));
|
||||
CFStringRef paths_array[] = { cf_path.get(), cf_dir_path.get() };
|
||||
ScopedCFTypeRef<CFArrayRef> watched_paths(
|
||||
CFArrayCreate(NULL, reinterpret_cast<const void**>(paths_array),
|
||||
base::size(paths_array), &kCFTypeArrayCallBacks));
|
||||
|
||||
FSEventStreamContext context;
|
||||
context.version = 0;
|
||||
context.info = this;
|
||||
context.retain = NULL;
|
||||
context.release = NULL;
|
||||
context.copyDescription = NULL;
|
||||
|
||||
fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
|
||||
watched_paths,
|
||||
start_event,
|
||||
kEventLatencySeconds,
|
||||
kFSEventStreamCreateFlagWatchRoot);
|
||||
FSEventStreamSetDispatchQueue(fsevent_stream_, queue_);
|
||||
|
||||
if (!FSEventStreamStart(fsevent_stream_)) {
|
||||
task_runner()->PostTask(FROM_HERE,
|
||||
BindOnce(&FilePathWatcherFSEvents::ReportError,
|
||||
weak_factory_.GetWeakPtr(), target_));
|
||||
}
|
||||
}
|
||||
|
||||
bool FilePathWatcherFSEvents::ResolveTargetPath() {
|
||||
FilePath resolved = ResolvePath(target_).StripTrailingSeparators();
|
||||
bool changed = resolved != resolved_target_;
|
||||
resolved_target_ = resolved;
|
||||
if (resolved_target_.empty()) {
|
||||
task_runner()->PostTask(FROM_HERE,
|
||||
BindOnce(&FilePathWatcherFSEvents::ReportError,
|
||||
weak_factory_.GetWeakPtr(), target_));
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void FilePathWatcherFSEvents::ReportError(const FilePath& target) {
|
||||
DCHECK(task_runner()->RunsTasksInCurrentSequence());
|
||||
if (!callback_.is_null()) {
|
||||
callback_.Run(target, true);
|
||||
}
|
||||
}
|
||||
|
||||
void FilePathWatcherFSEvents::DestroyEventStream() {
|
||||
FSEventStreamStop(fsevent_stream_);
|
||||
FSEventStreamInvalidate(fsevent_stream_);
|
||||
FSEventStreamRelease(fsevent_stream_);
|
||||
fsevent_stream_ = NULL;
|
||||
}
|
||||
|
||||
void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event,
|
||||
const FilePath& path) {
|
||||
DCHECK(resolved_target_.empty());
|
||||
|
||||
target_ = path;
|
||||
ResolveTargetPath();
|
||||
UpdateEventStream(start_event);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_
|
||||
#define BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_path_watcher.h"
|
||||
#include "base/mac/scoped_dispatch_object.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// Mac-specific file watcher implementation based on FSEvents.
|
||||
// There are trade-offs between the FSEvents implementation and a kqueue
|
||||
// implementation. The biggest issues are that FSEvents on 10.6 sometimes drops
|
||||
// events and kqueue does not trigger for modifications to a file in a watched
|
||||
// directory. See file_path_watcher_mac.cc for the code that decides when to
|
||||
// use which one.
|
||||
class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate {
|
||||
public:
|
||||
FilePathWatcherFSEvents();
|
||||
~FilePathWatcherFSEvents() override;
|
||||
|
||||
// FilePathWatcher::PlatformDelegate overrides.
|
||||
bool Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) override;
|
||||
void Cancel() override;
|
||||
|
||||
private:
|
||||
static void FSEventsCallback(ConstFSEventStreamRef stream,
|
||||
void* event_watcher,
|
||||
size_t num_events,
|
||||
void* event_paths,
|
||||
const FSEventStreamEventFlags flags[],
|
||||
const FSEventStreamEventId event_ids[]);
|
||||
|
||||
// Called from FSEventsCallback whenever there is a change to the paths.
|
||||
void OnFilePathsChanged(const std::vector<FilePath>& paths);
|
||||
|
||||
// Called on the task_runner() thread to dispatch path events. Can't access
|
||||
// target_ and resolved_target_ directly as those are modified on the
|
||||
// libdispatch thread.
|
||||
void DispatchEvents(const std::vector<FilePath>& paths,
|
||||
const FilePath& target,
|
||||
const FilePath& resolved_target);
|
||||
|
||||
// (Re-)Initialize the event stream to start reporting events from
|
||||
// |start_event|.
|
||||
void UpdateEventStream(FSEventStreamEventId start_event);
|
||||
|
||||
// Returns true if resolving the target path got a different result than
|
||||
// last time it was done.
|
||||
bool ResolveTargetPath();
|
||||
|
||||
// Report an error watching the given target.
|
||||
void ReportError(const FilePath& target);
|
||||
|
||||
// Destroy the event stream.
|
||||
void DestroyEventStream();
|
||||
|
||||
// Start watching the FSEventStream.
|
||||
void StartEventStream(FSEventStreamEventId start_event, const FilePath& path);
|
||||
|
||||
// Callback to notify upon changes.
|
||||
// (Only accessed from the task_runner() thread.)
|
||||
FilePathWatcher::Callback callback_;
|
||||
|
||||
// The dispatch queue on which the the event stream is scheduled.
|
||||
ScopedDispatchObject<dispatch_queue_t> queue_;
|
||||
|
||||
// Target path to watch (passed to callback).
|
||||
// (Only accessed from the libdispatch queue.)
|
||||
FilePath target_;
|
||||
|
||||
// Target path with all symbolic links resolved.
|
||||
// (Only accessed from the libdispatch queue.)
|
||||
FilePath resolved_target_;
|
||||
|
||||
// Backend stream we receive event callbacks from (strong reference).
|
||||
// (Only accessed from the libdispatch queue.)
|
||||
FSEventStreamRef fsevent_stream_;
|
||||
|
||||
WeakPtrFactory<FilePathWatcherFSEvents> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherFSEvents);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_path_watcher.h"
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
|
||||
public:
|
||||
FilePathWatcherImpl() {}
|
||||
~FilePathWatcherImpl() override {}
|
||||
|
||||
bool Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) override;
|
||||
|
||||
void Cancel() override;
|
||||
|
||||
private:
|
||||
FilePathWatcher::Callback callback_;
|
||||
FilePath target_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
|
||||
};
|
||||
|
||||
bool FilePathWatcherImpl::Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) {
|
||||
DCHECK(!callback.is_null());
|
||||
DCHECK(callback_.is_null());
|
||||
|
||||
callback_ = callback;
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::Cancel() {
|
||||
NOTIMPLEMENTED();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FilePathWatcher::FilePathWatcher() {
|
||||
sequence_checker_.DetachFromSequence();
|
||||
impl_ = std::make_unique<FilePathWatcherImpl>();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,377 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_path_watcher_kqueue.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/file_descriptor_posix.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
|
||||
// On some platforms these are not defined.
|
||||
#if !defined(EV_RECEIPT)
|
||||
#define EV_RECEIPT 0
|
||||
#endif
|
||||
#if !defined(O_EVTONLY)
|
||||
#define O_EVTONLY O_RDONLY
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {}
|
||||
|
||||
FilePathWatcherKQueue::~FilePathWatcherKQueue() {
|
||||
DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence());
|
||||
}
|
||||
|
||||
void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) {
|
||||
CloseFileDescriptor(&event.ident);
|
||||
EventData* entry = EventDataForKevent(event);
|
||||
delete entry;
|
||||
event.udata = NULL;
|
||||
}
|
||||
|
||||
int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) {
|
||||
// Make sure that we are working with a clean slate.
|
||||
DCHECK(events->empty());
|
||||
|
||||
std::vector<FilePath::StringType> components;
|
||||
path.GetComponents(&components);
|
||||
|
||||
if (components.size() < 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int last_existing_entry = 0;
|
||||
FilePath built_path;
|
||||
bool path_still_exists = true;
|
||||
for (std::vector<FilePath::StringType>::iterator i = components.begin();
|
||||
i != components.end(); ++i) {
|
||||
if (i == components.begin()) {
|
||||
built_path = FilePath(*i);
|
||||
} else {
|
||||
built_path = built_path.Append(*i);
|
||||
}
|
||||
uintptr_t fd = kNoFileDescriptor;
|
||||
if (path_still_exists) {
|
||||
fd = FileDescriptorForPath(built_path);
|
||||
if (fd == kNoFileDescriptor) {
|
||||
path_still_exists = false;
|
||||
} else {
|
||||
++last_existing_entry;
|
||||
}
|
||||
}
|
||||
FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : "";
|
||||
EventData* data = new EventData(built_path, subdir);
|
||||
struct kevent event;
|
||||
EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT),
|
||||
(NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB |
|
||||
NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data);
|
||||
events->push_back(event);
|
||||
}
|
||||
return last_existing_entry;
|
||||
}
|
||||
|
||||
uintptr_t FilePathWatcherKQueue::FileDescriptorForPath(const FilePath& path) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY));
|
||||
if (fd == kInvalidFd)
|
||||
return kNoFileDescriptor;
|
||||
return fd;
|
||||
}
|
||||
|
||||
void FilePathWatcherKQueue::CloseFileDescriptor(uintptr_t* fd) {
|
||||
if (*fd == kNoFileDescriptor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IGNORE_EINTR(close(*fd)) != 0) {
|
||||
DPLOG(ERROR) << "close";
|
||||
}
|
||||
*fd = kNoFileDescriptor;
|
||||
}
|
||||
|
||||
bool FilePathWatcherKQueue::AreKeventValuesValid(struct kevent* kevents,
|
||||
int count) {
|
||||
if (count < 0) {
|
||||
DPLOG(ERROR) << "kevent";
|
||||
return false;
|
||||
}
|
||||
bool valid = true;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (kevents[i].flags & EV_ERROR && kevents[i].data) {
|
||||
// Find the kevent in |events_| that matches the kevent with the error.
|
||||
EventVector::iterator event = events_.begin();
|
||||
for (; event != events_.end(); ++event) {
|
||||
if (event->ident == kevents[i].ident) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::string path_name;
|
||||
if (event != events_.end()) {
|
||||
EventData* event_data = EventDataForKevent(*event);
|
||||
if (event_data != NULL) {
|
||||
path_name = event_data->path_.value();
|
||||
}
|
||||
}
|
||||
if (path_name.empty()) {
|
||||
path_name = base::StringPrintf(
|
||||
"fd %ld", reinterpret_cast<long>(&kevents[i].ident));
|
||||
}
|
||||
DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name;
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
void FilePathWatcherKQueue::HandleAttributesChange(
|
||||
const EventVector::iterator& event,
|
||||
bool* target_file_affected,
|
||||
bool* update_watches) {
|
||||
EventVector::iterator next_event = event + 1;
|
||||
EventData* next_event_data = EventDataForKevent(*next_event);
|
||||
// Check to see if the next item in path is still accessible.
|
||||
uintptr_t have_access = FileDescriptorForPath(next_event_data->path_);
|
||||
if (have_access == kNoFileDescriptor) {
|
||||
*target_file_affected = true;
|
||||
*update_watches = true;
|
||||
EventVector::iterator local_event(event);
|
||||
for (; local_event != events_.end(); ++local_event) {
|
||||
// Close all nodes from the event down. This has the side effect of
|
||||
// potentially rendering other events in |updates| invalid.
|
||||
// There is no need to remove the events from |kqueue_| because this
|
||||
// happens as a side effect of closing the file descriptor.
|
||||
CloseFileDescriptor(&local_event->ident);
|
||||
}
|
||||
} else {
|
||||
CloseFileDescriptor(&have_access);
|
||||
}
|
||||
}
|
||||
|
||||
void FilePathWatcherKQueue::HandleDeleteOrMoveChange(
|
||||
const EventVector::iterator& event,
|
||||
bool* target_file_affected,
|
||||
bool* update_watches) {
|
||||
*target_file_affected = true;
|
||||
*update_watches = true;
|
||||
EventVector::iterator local_event(event);
|
||||
for (; local_event != events_.end(); ++local_event) {
|
||||
// Close all nodes from the event down. This has the side effect of
|
||||
// potentially rendering other events in |updates| invalid.
|
||||
// There is no need to remove the events from |kqueue_| because this
|
||||
// happens as a side effect of closing the file descriptor.
|
||||
CloseFileDescriptor(&local_event->ident);
|
||||
}
|
||||
}
|
||||
|
||||
void FilePathWatcherKQueue::HandleCreateItemChange(
|
||||
const EventVector::iterator& event,
|
||||
bool* target_file_affected,
|
||||
bool* update_watches) {
|
||||
// Get the next item in the path.
|
||||
EventVector::iterator next_event = event + 1;
|
||||
// Check to see if it already has a valid file descriptor.
|
||||
if (!IsKeventFileDescriptorOpen(*next_event)) {
|
||||
EventData* next_event_data = EventDataForKevent(*next_event);
|
||||
// If not, attempt to open a file descriptor for it.
|
||||
next_event->ident = FileDescriptorForPath(next_event_data->path_);
|
||||
if (IsKeventFileDescriptorOpen(*next_event)) {
|
||||
*update_watches = true;
|
||||
if (next_event_data->subdir_.empty()) {
|
||||
*target_file_affected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FilePathWatcherKQueue::UpdateWatches(bool* target_file_affected) {
|
||||
// Iterate over events adding kevents for items that exist to the kqueue.
|
||||
// Then check to see if new components in the path have been created.
|
||||
// Repeat until no new components in the path are detected.
|
||||
// This is to get around races in directory creation in a watched path.
|
||||
bool update_watches = true;
|
||||
while (update_watches) {
|
||||
size_t valid;
|
||||
for (valid = 0; valid < events_.size(); ++valid) {
|
||||
if (!IsKeventFileDescriptorOpen(events_[valid])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid == 0) {
|
||||
// The root of the file path is inaccessible?
|
||||
return false;
|
||||
}
|
||||
|
||||
EventVector updates(valid);
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], valid, &updates[0],
|
||||
valid, NULL));
|
||||
if (!AreKeventValuesValid(&updates[0], count)) {
|
||||
return false;
|
||||
}
|
||||
update_watches = false;
|
||||
for (; valid < events_.size(); ++valid) {
|
||||
EventData* event_data = EventDataForKevent(events_[valid]);
|
||||
events_[valid].ident = FileDescriptorForPath(event_data->path_);
|
||||
if (IsKeventFileDescriptorOpen(events_[valid])) {
|
||||
update_watches = true;
|
||||
if (event_data->subdir_.empty()) {
|
||||
*target_file_affected = true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilePathWatcherKQueue::Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) {
|
||||
DCHECK(target_.value().empty()); // Can only watch one path.
|
||||
DCHECK(!callback.is_null());
|
||||
DCHECK_EQ(kqueue_, -1);
|
||||
// Recursive watch is not supported using kqueue.
|
||||
DCHECK(!recursive);
|
||||
|
||||
callback_ = callback;
|
||||
target_ = path;
|
||||
|
||||
set_task_runner(SequencedTaskRunnerHandle::Get());
|
||||
|
||||
kqueue_ = kqueue();
|
||||
if (kqueue_ == -1) {
|
||||
DPLOG(ERROR) << "kqueue";
|
||||
return false;
|
||||
}
|
||||
|
||||
int last_entry = EventsForPath(target_, &events_);
|
||||
DCHECK_NE(last_entry, 0);
|
||||
|
||||
EventVector responses(last_entry);
|
||||
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry,
|
||||
&responses[0], last_entry, NULL));
|
||||
if (!AreKeventValuesValid(&responses[0], count)) {
|
||||
// Calling Cancel() here to close any file descriptors that were opened.
|
||||
// This would happen in the destructor anyways, but FilePathWatchers tend to
|
||||
// be long lived, and if an error has occurred, there is no reason to waste
|
||||
// the file descriptors.
|
||||
Cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's safe to use Unretained() because the watch is cancelled and the
|
||||
// callback cannot be invoked after |kqueue_watch_controller_| (which is a
|
||||
// member of |this|) has been deleted.
|
||||
kqueue_watch_controller_ = FileDescriptorWatcher::WatchReadable(
|
||||
kqueue_, BindRepeating(&FilePathWatcherKQueue::OnKQueueReadable,
|
||||
Unretained(this)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilePathWatcherKQueue::Cancel() {
|
||||
if (!task_runner()) {
|
||||
set_cancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(task_runner()->RunsTasksInCurrentSequence());
|
||||
if (!is_cancelled()) {
|
||||
set_cancelled();
|
||||
kqueue_watch_controller_.reset();
|
||||
if (IGNORE_EINTR(close(kqueue_)) != 0) {
|
||||
DPLOG(ERROR) << "close kqueue";
|
||||
}
|
||||
kqueue_ = -1;
|
||||
std::for_each(events_.begin(), events_.end(), ReleaseEvent);
|
||||
events_.clear();
|
||||
callback_.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void FilePathWatcherKQueue::OnKQueueReadable() {
|
||||
DCHECK(task_runner()->RunsTasksInCurrentSequence());
|
||||
DCHECK(events_.size());
|
||||
|
||||
// Request the file system update notifications that have occurred and return
|
||||
// them in |updates|. |count| will contain the number of updates that have
|
||||
// occurred.
|
||||
EventVector updates(events_.size());
|
||||
struct timespec timeout = {0, 0};
|
||||
int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(),
|
||||
&timeout));
|
||||
|
||||
// Error values are stored within updates, so check to make sure that no
|
||||
// errors occurred.
|
||||
if (!AreKeventValuesValid(&updates[0], count)) {
|
||||
callback_.Run(target_, true /* error */);
|
||||
Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
bool update_watches = false;
|
||||
bool send_notification = false;
|
||||
|
||||
// Iterate through each of the updates and react to them.
|
||||
for (int i = 0; i < count; ++i) {
|
||||
// Find our kevent record that matches the update notification.
|
||||
EventVector::iterator event = events_.begin();
|
||||
for (; event != events_.end(); ++event) {
|
||||
if (!IsKeventFileDescriptorOpen(*event) ||
|
||||
event->ident == updates[i].ident) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (event == events_.end() || !IsKeventFileDescriptorOpen(*event)) {
|
||||
// The event may no longer exist in |events_| because another event
|
||||
// modified |events_| in such a way to make it invalid. For example if
|
||||
// the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for
|
||||
// foo, bar and bam will be sent. If foo is processed first, then
|
||||
// the file descriptors for bar and bam will already be closed and set
|
||||
// to -1 before they get a chance to be processed.
|
||||
continue;
|
||||
}
|
||||
|
||||
EventData* event_data = EventDataForKevent(*event);
|
||||
|
||||
// If the subdir is empty, this is the last item on the path and is the
|
||||
// target file.
|
||||
bool target_file_affected = event_data->subdir_.empty();
|
||||
if ((updates[i].fflags & NOTE_ATTRIB) && !target_file_affected) {
|
||||
HandleAttributesChange(event, &target_file_affected, &update_watches);
|
||||
}
|
||||
if (updates[i].fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) {
|
||||
HandleDeleteOrMoveChange(event, &target_file_affected, &update_watches);
|
||||
}
|
||||
if ((updates[i].fflags & NOTE_WRITE) && !target_file_affected) {
|
||||
HandleCreateItemChange(event, &target_file_affected, &update_watches);
|
||||
}
|
||||
send_notification |= target_file_affected;
|
||||
}
|
||||
|
||||
if (update_watches) {
|
||||
if (!UpdateWatches(&send_notification)) {
|
||||
callback_.Run(target_, true /* error */);
|
||||
Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
if (send_notification) {
|
||||
callback_.Run(target_, false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_
|
||||
#define BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_
|
||||
|
||||
#include <sys/event.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_descriptor_watcher_posix.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_path_watcher.h"
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// Mac-specific file watcher implementation based on kqueue.
|
||||
// The Linux and Windows versions are able to detect:
|
||||
// - file creation/deletion/modification in a watched directory
|
||||
// - file creation/deletion/modification for a watched file
|
||||
// - modifications to the paths to a watched object that would affect the
|
||||
// object such as renaming/attibute changes etc.
|
||||
// The kqueue implementation will handle all of the items in the list above
|
||||
// except for detecting modifications to files in a watched directory. It will
|
||||
// detect the creation and deletion of files, just not the modification of
|
||||
// files. It does however detect the attribute changes that the FSEvents impl
|
||||
// would miss.
|
||||
class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate {
|
||||
public:
|
||||
FilePathWatcherKQueue();
|
||||
~FilePathWatcherKQueue() override;
|
||||
|
||||
// FilePathWatcher::PlatformDelegate overrides.
|
||||
bool Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) override;
|
||||
void Cancel() override;
|
||||
|
||||
private:
|
||||
class EventData {
|
||||
public:
|
||||
EventData(const FilePath& path, const FilePath::StringType& subdir)
|
||||
: path_(path), subdir_(subdir) { }
|
||||
FilePath path_; // Full path to this item.
|
||||
FilePath::StringType subdir_; // Path to any sub item.
|
||||
};
|
||||
|
||||
typedef std::vector<struct kevent> EventVector;
|
||||
|
||||
// Called when data is available in |kqueue_|.
|
||||
void OnKQueueReadable();
|
||||
|
||||
// Returns true if the kevent values are error free.
|
||||
bool AreKeventValuesValid(struct kevent* kevents, int count);
|
||||
|
||||
// Respond to a change of attributes of the path component represented by
|
||||
// |event|. Sets |target_file_affected| to true if |target_| is affected.
|
||||
// Sets |update_watches| to true if |events_| need to be updated.
|
||||
void HandleAttributesChange(const EventVector::iterator& event,
|
||||
bool* target_file_affected,
|
||||
bool* update_watches);
|
||||
|
||||
// Respond to a move or deletion of the path component represented by
|
||||
// |event|. Sets |target_file_affected| to true if |target_| is affected.
|
||||
// Sets |update_watches| to true if |events_| need to be updated.
|
||||
void HandleDeleteOrMoveChange(const EventVector::iterator& event,
|
||||
bool* target_file_affected,
|
||||
bool* update_watches);
|
||||
|
||||
// Respond to a creation of an item in the path component represented by
|
||||
// |event|. Sets |target_file_affected| to true if |target_| is affected.
|
||||
// Sets |update_watches| to true if |events_| need to be updated.
|
||||
void HandleCreateItemChange(const EventVector::iterator& event,
|
||||
bool* target_file_affected,
|
||||
bool* update_watches);
|
||||
|
||||
// Update |events_| with the current status of the system.
|
||||
// Sets |target_file_affected| to true if |target_| is affected.
|
||||
// Returns false if an error occurs.
|
||||
bool UpdateWatches(bool* target_file_affected);
|
||||
|
||||
// Fills |events| with one kevent per component in |path|.
|
||||
// Returns the number of valid events created where a valid event is
|
||||
// defined as one that has a ident (file descriptor) field != -1.
|
||||
static int EventsForPath(FilePath path, EventVector *events);
|
||||
|
||||
// Release a kevent generated by EventsForPath.
|
||||
static void ReleaseEvent(struct kevent& event);
|
||||
|
||||
// Returns a file descriptor that will not block the system from deleting
|
||||
// the file it references.
|
||||
static uintptr_t FileDescriptorForPath(const FilePath& path);
|
||||
|
||||
static const uintptr_t kNoFileDescriptor = static_cast<uintptr_t>(-1);
|
||||
|
||||
// Closes |*fd| and sets |*fd| to -1.
|
||||
static void CloseFileDescriptor(uintptr_t* fd);
|
||||
|
||||
// Returns true if kevent has open file descriptor.
|
||||
static bool IsKeventFileDescriptorOpen(const struct kevent& event) {
|
||||
return event.ident != kNoFileDescriptor;
|
||||
}
|
||||
|
||||
static EventData* EventDataForKevent(const struct kevent& event) {
|
||||
return reinterpret_cast<EventData*>(event.udata);
|
||||
}
|
||||
|
||||
EventVector events_;
|
||||
FilePathWatcher::Callback callback_;
|
||||
FilePath target_;
|
||||
int kqueue_;
|
||||
|
||||
// Throughout the lifetime of this, OnKQueueReadable() will be called when
|
||||
// data is available in |kqueue_|.
|
||||
std::unique_ptr<FileDescriptorWatcher::Controller> kqueue_watch_controller_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherKQueue);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_
|
||||
|
|
@ -0,0 +1,749 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_path_watcher.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/files/file_enumerator.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/location.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
// The /proc path to max_user_watches.
|
||||
constexpr char kInotifyMaxUserWatchesPath[] =
|
||||
"/proc/sys/fs/inotify/max_user_watches";
|
||||
|
||||
// This is a soft limit. If there are more than |kExpectedFilePathWatches|
|
||||
// FilePathWatchers for a user, than they might affect each other's inotify
|
||||
// watchers limit.
|
||||
constexpr int kExpectedFilePathWatchers = 16;
|
||||
|
||||
// The default max inotify watchers limit per user, if reading
|
||||
// /proc/sys/fs/inotify/max_user_watches fails.
|
||||
constexpr int kDefaultInotifyMaxUserWatches = 8192;
|
||||
|
||||
class FilePathWatcherImpl;
|
||||
class InotifyReader;
|
||||
|
||||
// Get the maximum number of inotify watches can be used by a FilePathWatcher
|
||||
// instance. This is based on /proc/sys/fs/inotify/max_user_watches entry.
|
||||
int GetMaxNumberOfInotifyWatches() {
|
||||
const static int max = []() {
|
||||
int max_number_of_inotify_watches = 0;
|
||||
|
||||
std::ifstream in(kInotifyMaxUserWatchesPath);
|
||||
if (!in.is_open() || !(in >> max_number_of_inotify_watches)) {
|
||||
LOG(ERROR) << "Failed to read " << kInotifyMaxUserWatchesPath;
|
||||
return kDefaultInotifyMaxUserWatches / kExpectedFilePathWatchers;
|
||||
}
|
||||
|
||||
return max_number_of_inotify_watches / kExpectedFilePathWatchers;
|
||||
}();
|
||||
return max;
|
||||
}
|
||||
|
||||
class InotifyReaderThreadDelegate final : public PlatformThread::Delegate {
|
||||
public:
|
||||
explicit InotifyReaderThreadDelegate(int inotify_fd)
|
||||
: inotify_fd_(inotify_fd) {}
|
||||
~InotifyReaderThreadDelegate() override = default;
|
||||
|
||||
private:
|
||||
void ThreadMain() override;
|
||||
|
||||
const int inotify_fd_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(InotifyReaderThreadDelegate);
|
||||
};
|
||||
|
||||
// Singleton to manage all inotify watches.
|
||||
// TODO(tony): It would be nice if this wasn't a singleton.
|
||||
// http://crbug.com/38174
|
||||
class InotifyReader {
|
||||
public:
|
||||
using Watch = int; // Watch descriptor used by AddWatch() and RemoveWatch().
|
||||
static constexpr Watch kInvalidWatch = -1;
|
||||
static constexpr Watch kWatchLimitExceeded = -2;
|
||||
|
||||
// Watch directory |path| for changes. |watcher| will be notified on each
|
||||
// change. Returns |kInvalidWatch| on failure.
|
||||
Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher);
|
||||
|
||||
// Remove |watch| if it's valid.
|
||||
void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher);
|
||||
|
||||
// Callback for InotifyReaderTask.
|
||||
void OnInotifyEvent(const inotify_event* event);
|
||||
|
||||
private:
|
||||
friend struct LazyInstanceTraitsBase<InotifyReader>;
|
||||
|
||||
InotifyReader();
|
||||
// There is no destructor because |g_inotify_reader| is a
|
||||
// base::LazyInstace::Leaky object. Having a destructor causes build
|
||||
// issues with GCC 6 (http://crbug.com/636346).
|
||||
|
||||
// Returns true on successful thread creation.
|
||||
bool StartThread();
|
||||
|
||||
// Lock to protect |watchers_|.
|
||||
Lock lock_;
|
||||
|
||||
// We keep track of which delegates want to be notified on which watches.
|
||||
std::unordered_map<Watch, std::set<FilePathWatcherImpl*>> watchers_;
|
||||
|
||||
// File descriptor returned by inotify_init.
|
||||
const int inotify_fd_;
|
||||
|
||||
// Thread delegate for the Inotify thread.
|
||||
InotifyReaderThreadDelegate thread_delegate_;
|
||||
|
||||
// Flag set to true when startup was successful.
|
||||
bool valid_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(InotifyReader);
|
||||
};
|
||||
|
||||
class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
|
||||
public:
|
||||
FilePathWatcherImpl();
|
||||
~FilePathWatcherImpl() override;
|
||||
|
||||
// Called for each event coming from the watch. |fired_watch| identifies the
|
||||
// watch that fired, |child| indicates what has changed, and is relative to
|
||||
// the currently watched path for |fired_watch|.
|
||||
//
|
||||
// |created| is true if the object appears.
|
||||
// |deleted| is true if the object disappears.
|
||||
// |is_dir| is true if the object is a directory.
|
||||
void OnFilePathChanged(InotifyReader::Watch fired_watch,
|
||||
const FilePath::StringType& child,
|
||||
bool created,
|
||||
bool deleted,
|
||||
bool is_dir);
|
||||
|
||||
// Increase the number of inotify watches associated to this
|
||||
// FilePathWatcherImpl instance.
|
||||
bool IncreaseWatch();
|
||||
|
||||
// Decrease the number of inotify watches associated to this
|
||||
// FilePathWatcherImpl instance.
|
||||
void DecreaseWatch();
|
||||
|
||||
private:
|
||||
void OnFilePathChangedOnOriginSequence(InotifyReader::Watch fired_watch,
|
||||
const FilePath::StringType& child,
|
||||
bool created,
|
||||
bool deleted,
|
||||
bool is_dir);
|
||||
|
||||
// Start watching |path| for changes and notify |delegate| on each change.
|
||||
// Returns true if watch for |path| has been added successfully.
|
||||
bool Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) override;
|
||||
|
||||
// Cancel the watch. This unregisters the instance with InotifyReader.
|
||||
void Cancel() override;
|
||||
|
||||
// Inotify watches are installed for all directory components of |target_|.
|
||||
// A WatchEntry instance holds:
|
||||
// - |watch|: the watch descriptor for a component.
|
||||
// - |subdir|: the subdirectory that identifies the next component.
|
||||
// - For the last component, there is no next component, so it is empty.
|
||||
// - |linkname|: the target of the symlink.
|
||||
// - Only if the target being watched is a symbolic link.
|
||||
struct WatchEntry {
|
||||
explicit WatchEntry(const FilePath::StringType& dirname)
|
||||
: watch(InotifyReader::kInvalidWatch),
|
||||
subdir(dirname) {}
|
||||
|
||||
InotifyReader::Watch watch;
|
||||
FilePath::StringType subdir;
|
||||
FilePath::StringType linkname;
|
||||
};
|
||||
|
||||
// Reconfigure to watch for the most specific parent directory of |target_|
|
||||
// that exists. Also calls UpdateRecursiveWatches() below.
|
||||
void UpdateWatches();
|
||||
|
||||
// Reconfigure to recursively watch |target_| and all its sub-directories.
|
||||
// - This is a no-op if the watch is not recursive.
|
||||
// - If |target_| does not exist, then clear all the recursive watches.
|
||||
// - Assuming |target_| exists, passing kInvalidWatch as |fired_watch| forces
|
||||
// addition of recursive watches for |target_|.
|
||||
// - Otherwise, only the directory associated with |fired_watch| and its
|
||||
// sub-directories will be reconfigured.
|
||||
void UpdateRecursiveWatches(InotifyReader::Watch fired_watch, bool is_dir);
|
||||
|
||||
// Enumerate recursively through |path| and add / update watches.
|
||||
void UpdateRecursiveWatchesForPath(const FilePath& path);
|
||||
|
||||
// Do internal bookkeeping to update mappings between |watch| and its
|
||||
// associated full path |path|.
|
||||
void TrackWatchForRecursion(InotifyReader::Watch watch, const FilePath& path);
|
||||
|
||||
// Remove all the recursive watches.
|
||||
void RemoveRecursiveWatches();
|
||||
|
||||
// |path| is a symlink to a non-existent target. Attempt to add a watch to
|
||||
// the link target's parent directory. Update |watch_entry| on success.
|
||||
void AddWatchForBrokenSymlink(const FilePath& path, WatchEntry* watch_entry);
|
||||
|
||||
bool HasValidWatchVector() const;
|
||||
|
||||
// Callback to notify upon changes.
|
||||
FilePathWatcher::Callback callback_;
|
||||
|
||||
// The file or directory we're supposed to watch.
|
||||
FilePath target_;
|
||||
|
||||
bool recursive_ = false;
|
||||
|
||||
// The vector of watches and next component names for all path components,
|
||||
// starting at the root directory. The last entry corresponds to the watch for
|
||||
// |target_| and always stores an empty next component name in |subdir|.
|
||||
std::vector<WatchEntry> watches_;
|
||||
|
||||
// The number of inotify watches currently associated with this instance.
|
||||
int number_of_inotify_watches_ = 0;
|
||||
|
||||
std::unordered_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_;
|
||||
std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_;
|
||||
|
||||
// Read only while INotifyReader::lock_ is held, and used to post asynchronous
|
||||
// notifications to the Watcher on its home task_runner(). Ideally this should
|
||||
// be const, but since it is initialized from |weak_factory_|, which must
|
||||
// appear after it, that is not possible.
|
||||
WeakPtr<FilePathWatcherImpl> weak_ptr_;
|
||||
|
||||
WeakPtrFactory<FilePathWatcherImpl> weak_factory_{this};
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
|
||||
};
|
||||
|
||||
LazyInstance<InotifyReader>::Leaky g_inotify_reader = LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
void InotifyReaderThreadDelegate::ThreadMain() {
|
||||
PlatformThread::SetName("inotify_reader");
|
||||
|
||||
// Make sure the file descriptors are good for use with select().
|
||||
CHECK_LE(0, inotify_fd_);
|
||||
CHECK_GT(FD_SETSIZE, inotify_fd_);
|
||||
|
||||
while (true) {
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(inotify_fd_, &rfds);
|
||||
|
||||
// Wait until some inotify events are available.
|
||||
int select_result =
|
||||
HANDLE_EINTR(select(inotify_fd_ + 1, &rfds, nullptr, nullptr, nullptr));
|
||||
if (select_result < 0) {
|
||||
DPLOG(WARNING) << "select failed";
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust buffer size to current event queue size.
|
||||
int buffer_size;
|
||||
int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd_, FIONREAD, &buffer_size));
|
||||
|
||||
if (ioctl_result != 0) {
|
||||
DPLOG(WARNING) << "ioctl failed";
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<char> buffer(buffer_size);
|
||||
|
||||
ssize_t bytes_read =
|
||||
HANDLE_EINTR(read(inotify_fd_, &buffer[0], buffer_size));
|
||||
|
||||
if (bytes_read < 0) {
|
||||
DPLOG(WARNING) << "read from inotify fd failed";
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t i = 0;
|
||||
while (i < bytes_read) {
|
||||
inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]);
|
||||
size_t event_size = sizeof(inotify_event) + event->len;
|
||||
DCHECK(i + event_size <= static_cast<size_t>(bytes_read));
|
||||
g_inotify_reader.Get().OnInotifyEvent(event);
|
||||
i += event_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InotifyReader::InotifyReader()
|
||||
: inotify_fd_(inotify_init()), thread_delegate_(inotify_fd_) {
|
||||
if (inotify_fd_ < 0) {
|
||||
PLOG(ERROR) << "inotify_init() failed";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StartThread())
|
||||
return;
|
||||
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
bool InotifyReader::StartThread() {
|
||||
// This object is LazyInstance::Leaky, so thread_delegate_ will outlive the
|
||||
// thread.
|
||||
return PlatformThread::CreateNonJoinable(0, &thread_delegate_);
|
||||
}
|
||||
|
||||
InotifyReader::Watch InotifyReader::AddWatch(
|
||||
const FilePath& path, FilePathWatcherImpl* watcher) {
|
||||
if (!valid_)
|
||||
return kInvalidWatch;
|
||||
|
||||
AutoLock auto_lock(lock_);
|
||||
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::WILL_BLOCK);
|
||||
|
||||
if (!watcher->IncreaseWatch())
|
||||
return kWatchLimitExceeded;
|
||||
Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(),
|
||||
IN_ATTRIB | IN_CREATE | IN_DELETE |
|
||||
IN_CLOSE_WRITE | IN_MOVE |
|
||||
IN_ONLYDIR);
|
||||
|
||||
if (watch == kInvalidWatch) {
|
||||
// This watch shouldn't be counted.
|
||||
watcher->DecreaseWatch();
|
||||
return kInvalidWatch;
|
||||
}
|
||||
|
||||
watchers_[watch].insert(watcher);
|
||||
|
||||
return watch;
|
||||
}
|
||||
|
||||
void InotifyReader::RemoveWatch(Watch watch, FilePathWatcherImpl* watcher) {
|
||||
if (!valid_ || (watch == kInvalidWatch))
|
||||
return;
|
||||
|
||||
AutoLock auto_lock(lock_);
|
||||
|
||||
watchers_[watch].erase(watcher);
|
||||
watcher->DecreaseWatch();
|
||||
|
||||
if (watchers_[watch].empty()) {
|
||||
watchers_.erase(watch);
|
||||
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE,
|
||||
BlockingType::WILL_BLOCK);
|
||||
inotify_rm_watch(inotify_fd_, watch);
|
||||
}
|
||||
}
|
||||
|
||||
void InotifyReader::OnInotifyEvent(const inotify_event* event) {
|
||||
if (event->mask & IN_IGNORED)
|
||||
return;
|
||||
|
||||
FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL(""));
|
||||
AutoLock auto_lock(lock_);
|
||||
|
||||
auto& watcher_set = watchers_[event->wd];
|
||||
for (FilePathWatcherImpl* watcher : watcher_set) {
|
||||
watcher->OnFilePathChanged(
|
||||
event->wd, child, event->mask & (IN_CREATE | IN_MOVED_TO),
|
||||
event->mask & (IN_DELETE | IN_MOVED_FROM), event->mask & IN_ISDIR);
|
||||
}
|
||||
}
|
||||
|
||||
FilePathWatcherImpl::FilePathWatcherImpl() {
|
||||
weak_ptr_ = weak_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
FilePathWatcherImpl::~FilePathWatcherImpl() {
|
||||
DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence());
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch,
|
||||
const FilePath::StringType& child,
|
||||
bool created,
|
||||
bool deleted,
|
||||
bool is_dir) {
|
||||
DCHECK(!task_runner()->RunsTasksInCurrentSequence());
|
||||
|
||||
// This method is invoked on the Inotify thread. Switch to task_runner() to
|
||||
// access |watches_| safely. Use a WeakPtr to prevent the callback from
|
||||
// running after |this| is destroyed (i.e. after the watch is cancelled).
|
||||
task_runner()->PostTask(
|
||||
FROM_HERE,
|
||||
BindOnce(&FilePathWatcherImpl::OnFilePathChangedOnOriginSequence,
|
||||
weak_ptr_, fired_watch, child, created, deleted, is_dir));
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::OnFilePathChangedOnOriginSequence(
|
||||
InotifyReader::Watch fired_watch,
|
||||
const FilePath::StringType& child,
|
||||
bool created,
|
||||
bool deleted,
|
||||
bool is_dir) {
|
||||
DCHECK(task_runner()->RunsTasksInCurrentSequence());
|
||||
DCHECK(!watches_.empty());
|
||||
DCHECK(HasValidWatchVector());
|
||||
|
||||
// Used below to avoid multiple recursive updates.
|
||||
bool did_update = false;
|
||||
|
||||
// Find the entry in |watches_| that corresponds to |fired_watch|.
|
||||
for (size_t i = 0; i < watches_.size(); ++i) {
|
||||
const WatchEntry& watch_entry = watches_[i];
|
||||
if (fired_watch != watch_entry.watch)
|
||||
continue;
|
||||
|
||||
// Check whether a path component of |target_| changed.
|
||||
bool change_on_target_path =
|
||||
child.empty() ||
|
||||
(child == watch_entry.linkname) ||
|
||||
(child == watch_entry.subdir);
|
||||
|
||||
// Check if the change references |target_| or a direct child of |target_|.
|
||||
bool target_changed;
|
||||
if (watch_entry.subdir.empty()) {
|
||||
// The fired watch is for a WatchEntry without a subdir. Thus for a given
|
||||
// |target_| = "/path/to/foo", this is for "foo". Here, check either:
|
||||
// - the target has no symlink: it is the target and it changed.
|
||||
// - the target has a symlink, and it matches |child|.
|
||||
target_changed = (watch_entry.linkname.empty() ||
|
||||
child == watch_entry.linkname);
|
||||
} else {
|
||||
// The fired watch is for a WatchEntry with a subdir. Thus for a given
|
||||
// |target_| = "/path/to/foo", this is for {"/", "/path", "/path/to"}.
|
||||
// So we can safely access the next WatchEntry since we have not reached
|
||||
// the end yet. Check |watch_entry| is for "/path/to", i.e. the next
|
||||
// element is "foo".
|
||||
bool next_watch_may_be_for_target = watches_[i + 1].subdir.empty();
|
||||
if (next_watch_may_be_for_target) {
|
||||
// The current |watch_entry| is for "/path/to", so check if the |child|
|
||||
// that changed is "foo".
|
||||
target_changed = watch_entry.subdir == child;
|
||||
} else {
|
||||
// The current |watch_entry| is not for "/path/to", so the next entry
|
||||
// cannot be "foo". Thus |target_| has not changed.
|
||||
target_changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update watches if a directory component of the |target_| path
|
||||
// (dis)appears. Note that we don't add the additional restriction of
|
||||
// checking the event mask to see if it is for a directory here as changes
|
||||
// to symlinks on the target path will not have IN_ISDIR set in the event
|
||||
// masks. As a result we may sometimes call UpdateWatches() unnecessarily.
|
||||
if (change_on_target_path && (created || deleted) && !did_update) {
|
||||
UpdateWatches();
|
||||
did_update = true;
|
||||
}
|
||||
|
||||
// Report the following events:
|
||||
// - The target or a direct child of the target got changed (in case the
|
||||
// watched path refers to a directory).
|
||||
// - One of the parent directories got moved or deleted, since the target
|
||||
// disappears in this case.
|
||||
// - One of the parent directories appears. The event corresponding to
|
||||
// the target appearing might have been missed in this case, so recheck.
|
||||
if (target_changed ||
|
||||
(change_on_target_path && deleted) ||
|
||||
(change_on_target_path && created && PathExists(target_))) {
|
||||
if (!did_update) {
|
||||
UpdateRecursiveWatches(fired_watch, is_dir);
|
||||
did_update = true;
|
||||
}
|
||||
callback_.Run(target_, false /* error */);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Contains(recursive_paths_by_watch_, fired_watch)) {
|
||||
if (!did_update)
|
||||
UpdateRecursiveWatches(fired_watch, is_dir);
|
||||
callback_.Run(target_, false /* error */);
|
||||
}
|
||||
}
|
||||
|
||||
bool FilePathWatcherImpl::IncreaseWatch() {
|
||||
if (number_of_inotify_watches_ >= GetMaxNumberOfInotifyWatches()) {
|
||||
// Notify that error happened since we are hitting the inotify watches
|
||||
// limit.
|
||||
callback_.Run(target_, true /* error */);
|
||||
return false;
|
||||
}
|
||||
++number_of_inotify_watches_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::DecreaseWatch() {
|
||||
--number_of_inotify_watches_;
|
||||
// Sanity check:
|
||||
DCHECK_GE(number_of_inotify_watches_, 0);
|
||||
}
|
||||
|
||||
bool FilePathWatcherImpl::Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) {
|
||||
DCHECK(target_.empty());
|
||||
|
||||
set_task_runner(SequencedTaskRunnerHandle::Get());
|
||||
callback_ = callback;
|
||||
target_ = path;
|
||||
recursive_ = recursive;
|
||||
|
||||
std::vector<FilePath::StringType> comps;
|
||||
target_.GetComponents(&comps);
|
||||
DCHECK(!comps.empty());
|
||||
for (size_t i = 1; i < comps.size(); ++i)
|
||||
watches_.push_back(WatchEntry(comps[i]));
|
||||
watches_.push_back(WatchEntry(FilePath::StringType()));
|
||||
UpdateWatches();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::Cancel() {
|
||||
if (!callback_) {
|
||||
// Watch() was never called.
|
||||
set_cancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(task_runner()->RunsTasksInCurrentSequence());
|
||||
DCHECK(!is_cancelled());
|
||||
|
||||
set_cancelled();
|
||||
callback_.Reset();
|
||||
|
||||
for (const auto& watch : watches_)
|
||||
g_inotify_reader.Get().RemoveWatch(watch.watch, this);
|
||||
watches_.clear();
|
||||
target_.clear();
|
||||
RemoveRecursiveWatches();
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::UpdateWatches() {
|
||||
// Ensure this runs on the task_runner() exclusively in order to avoid
|
||||
// concurrency issues.
|
||||
DCHECK(task_runner()->RunsTasksInCurrentSequence());
|
||||
DCHECK(HasValidWatchVector());
|
||||
|
||||
// Walk the list of watches and update them as we go.
|
||||
FilePath path(FILE_PATH_LITERAL("/"));
|
||||
for (WatchEntry& watch_entry : watches_) {
|
||||
InotifyReader::Watch old_watch = watch_entry.watch;
|
||||
watch_entry.watch = InotifyReader::kInvalidWatch;
|
||||
watch_entry.linkname.clear();
|
||||
watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this);
|
||||
if (watch_entry.watch == InotifyReader::kWatchLimitExceeded)
|
||||
break;
|
||||
if (watch_entry.watch == InotifyReader::kInvalidWatch) {
|
||||
// Ignore the error code (beyond symlink handling) to attempt to add
|
||||
// watches on accessible children of unreadable directories. Note that
|
||||
// this is a best-effort attempt; we may not catch events in this
|
||||
// scenario.
|
||||
if (IsLink(path))
|
||||
AddWatchForBrokenSymlink(path, &watch_entry);
|
||||
}
|
||||
if (old_watch != watch_entry.watch)
|
||||
g_inotify_reader.Get().RemoveWatch(old_watch, this);
|
||||
path = path.Append(watch_entry.subdir);
|
||||
}
|
||||
|
||||
UpdateRecursiveWatches(InotifyReader::kInvalidWatch,
|
||||
false /* is directory? */);
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::UpdateRecursiveWatches(
|
||||
InotifyReader::Watch fired_watch,
|
||||
bool is_dir) {
|
||||
DCHECK(HasValidWatchVector());
|
||||
|
||||
if (!recursive_)
|
||||
return;
|
||||
|
||||
if (!DirectoryExists(target_)) {
|
||||
RemoveRecursiveWatches();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to see if this is a forced update or if some component of |target_|
|
||||
// has changed. For these cases, redo the watches for |target_| and below.
|
||||
if (!Contains(recursive_paths_by_watch_, fired_watch) &&
|
||||
fired_watch != watches_.back().watch) {
|
||||
UpdateRecursiveWatchesForPath(target_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Underneath |target_|, only directory changes trigger watch updates.
|
||||
if (!is_dir)
|
||||
return;
|
||||
|
||||
const FilePath& changed_dir = Contains(recursive_paths_by_watch_, fired_watch)
|
||||
? recursive_paths_by_watch_[fired_watch]
|
||||
: target_;
|
||||
|
||||
auto start_it = recursive_watches_by_path_.lower_bound(changed_dir);
|
||||
auto end_it = start_it;
|
||||
for (; end_it != recursive_watches_by_path_.end(); ++end_it) {
|
||||
const FilePath& cur_path = end_it->first;
|
||||
if (!changed_dir.IsParent(cur_path))
|
||||
break;
|
||||
if (!DirectoryExists(cur_path))
|
||||
g_inotify_reader.Get().RemoveWatch(end_it->second, this);
|
||||
|
||||
// Keep it in sync with |recursive_watches_by_path_| crbug.com/995196.
|
||||
recursive_paths_by_watch_.erase(end_it->second);
|
||||
}
|
||||
recursive_watches_by_path_.erase(start_it, end_it);
|
||||
UpdateRecursiveWatchesForPath(changed_dir);
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::UpdateRecursiveWatchesForPath(const FilePath& path) {
|
||||
DCHECK(recursive_);
|
||||
DCHECK(!path.empty());
|
||||
DCHECK(DirectoryExists(path));
|
||||
|
||||
// Note: SHOW_SYM_LINKS exposes symlinks as symlinks, so they are ignored
|
||||
// rather than followed. Following symlinks can easily lead to the undesirable
|
||||
// situation where the entire file system is being watched.
|
||||
FileEnumerator enumerator(
|
||||
path,
|
||||
true /* recursive enumeration */,
|
||||
FileEnumerator::DIRECTORIES | FileEnumerator::SHOW_SYM_LINKS);
|
||||
for (FilePath current = enumerator.Next();
|
||||
!current.empty();
|
||||
current = enumerator.Next()) {
|
||||
DCHECK(enumerator.GetInfo().IsDirectory());
|
||||
|
||||
if (!Contains(recursive_watches_by_path_, current)) {
|
||||
// Add new watches.
|
||||
InotifyReader::Watch watch =
|
||||
g_inotify_reader.Get().AddWatch(current, this);
|
||||
if (watch == InotifyReader::kWatchLimitExceeded)
|
||||
break;
|
||||
TrackWatchForRecursion(watch, current);
|
||||
} else {
|
||||
// Update existing watches.
|
||||
InotifyReader::Watch old_watch = recursive_watches_by_path_[current];
|
||||
DCHECK_NE(InotifyReader::kInvalidWatch, old_watch);
|
||||
InotifyReader::Watch watch =
|
||||
g_inotify_reader.Get().AddWatch(current, this);
|
||||
if (watch == InotifyReader::kWatchLimitExceeded)
|
||||
break;
|
||||
if (watch != old_watch) {
|
||||
g_inotify_reader.Get().RemoveWatch(old_watch, this);
|
||||
recursive_paths_by_watch_.erase(old_watch);
|
||||
recursive_watches_by_path_.erase(current);
|
||||
TrackWatchForRecursion(watch, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::TrackWatchForRecursion(InotifyReader::Watch watch,
|
||||
const FilePath& path) {
|
||||
DCHECK(recursive_);
|
||||
DCHECK(!path.empty());
|
||||
DCHECK(target_.IsParent(path));
|
||||
|
||||
if (watch == InotifyReader::kInvalidWatch)
|
||||
return;
|
||||
|
||||
DCHECK(!Contains(recursive_paths_by_watch_, watch));
|
||||
DCHECK(!Contains(recursive_watches_by_path_, path));
|
||||
recursive_paths_by_watch_[watch] = path;
|
||||
recursive_watches_by_path_[path] = watch;
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::RemoveRecursiveWatches() {
|
||||
if (!recursive_)
|
||||
return;
|
||||
|
||||
for (const auto& it : recursive_paths_by_watch_)
|
||||
g_inotify_reader.Get().RemoveWatch(it.first, this);
|
||||
|
||||
recursive_paths_by_watch_.clear();
|
||||
recursive_watches_by_path_.clear();
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path,
|
||||
WatchEntry* watch_entry) {
|
||||
DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch);
|
||||
FilePath link;
|
||||
if (!ReadSymbolicLink(path, &link))
|
||||
return;
|
||||
|
||||
if (!link.IsAbsolute())
|
||||
link = path.DirName().Append(link);
|
||||
|
||||
// Try watching symlink target directory. If the link target is "/", then we
|
||||
// shouldn't get here in normal situations and if we do, we'd watch "/" for
|
||||
// changes to a component "/" which is harmless so no special treatment of
|
||||
// this case is required.
|
||||
InotifyReader::Watch watch =
|
||||
g_inotify_reader.Get().AddWatch(link.DirName(), this);
|
||||
if (watch == InotifyReader::kInvalidWatch) {
|
||||
// TODO(craig) Symlinks only work if the parent directory for the target
|
||||
// exist. Ideally we should make sure we've watched all the components of
|
||||
// the symlink path for changes. See crbug.com/91561 for details.
|
||||
DPLOG(WARNING) << "Watch failed for " << link.DirName().value();
|
||||
return;
|
||||
}
|
||||
watch_entry->watch = watch;
|
||||
watch_entry->linkname = link.BaseName().value();
|
||||
}
|
||||
|
||||
bool FilePathWatcherImpl::HasValidWatchVector() const {
|
||||
if (watches_.empty())
|
||||
return false;
|
||||
for (size_t i = 0; i < watches_.size() - 1; ++i) {
|
||||
if (watches_[i].subdir.empty())
|
||||
return false;
|
||||
}
|
||||
return watches_.back().subdir.empty();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FilePathWatcher::FilePathWatcher() {
|
||||
sequence_checker_.DetachFromSequence();
|
||||
impl_ = std::make_unique<FilePathWatcherImpl>();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/files/file_path_watcher.h"
|
||||
#include "base/files/file_path_watcher_kqueue.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if !defined(OS_IOS)
|
||||
#include "base/files/file_path_watcher_fsevents.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
|
||||
public:
|
||||
FilePathWatcherImpl() = default;
|
||||
~FilePathWatcherImpl() override = default;
|
||||
|
||||
bool Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) override {
|
||||
// Use kqueue for non-recursive watches and FSEvents for recursive ones.
|
||||
DCHECK(!impl_.get());
|
||||
if (recursive) {
|
||||
if (!FilePathWatcher::RecursiveWatchAvailable())
|
||||
return false;
|
||||
#if !defined(OS_IOS)
|
||||
impl_ = std::make_unique<FilePathWatcherFSEvents>();
|
||||
#endif // OS_IOS
|
||||
} else {
|
||||
impl_ = std::make_unique<FilePathWatcherKQueue>();
|
||||
}
|
||||
DCHECK(impl_.get());
|
||||
return impl_->Watch(path, recursive, callback);
|
||||
}
|
||||
|
||||
void Cancel() override {
|
||||
if (impl_.get())
|
||||
impl_->Cancel();
|
||||
set_cancelled();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<PlatformDelegate> impl_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
FilePathWatcher::FilePathWatcher() {
|
||||
sequence_checker_.DetachFromSequence();
|
||||
impl_ = std::make_unique<FilePathWatcherImpl>();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This file exists for Unix systems which don't have the inotify headers, and
|
||||
// thus cannot build file_watcher_inotify.cc
|
||||
|
||||
#include "base/files/file_path_watcher.h"
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
|
||||
public:
|
||||
FilePathWatcherImpl() = default;
|
||||
~FilePathWatcherImpl() override = default;
|
||||
|
||||
bool Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Cancel() override {}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
FilePathWatcher::FilePathWatcher() {
|
||||
sequence_checker_.DetachFromSequence();
|
||||
impl_ = std::make_unique<FilePathWatcherImpl>();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,301 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_path_watcher.h"
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/win/object_watcher.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
|
||||
public base::win::ObjectWatcher::Delegate {
|
||||
public:
|
||||
FilePathWatcherImpl()
|
||||
: handle_(INVALID_HANDLE_VALUE),
|
||||
recursive_watch_(false) {}
|
||||
~FilePathWatcherImpl() override;
|
||||
|
||||
// FilePathWatcher::PlatformDelegate:
|
||||
bool Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) override;
|
||||
void Cancel() override;
|
||||
|
||||
// base::win::ObjectWatcher::Delegate:
|
||||
void OnObjectSignaled(HANDLE object) override;
|
||||
|
||||
private:
|
||||
// Setup a watch handle for directory |dir|. Set |recursive| to true to watch
|
||||
// the directory sub trees. Returns true if no fatal error occurs. |handle|
|
||||
// will receive the handle value if |dir| is watchable, otherwise
|
||||
// INVALID_HANDLE_VALUE.
|
||||
static bool SetupWatchHandle(const FilePath& dir,
|
||||
bool recursive,
|
||||
HANDLE* handle) WARN_UNUSED_RESULT;
|
||||
|
||||
// (Re-)Initialize the watch handle.
|
||||
bool UpdateWatch() WARN_UNUSED_RESULT;
|
||||
|
||||
// Destroy the watch handle.
|
||||
void DestroyWatch();
|
||||
|
||||
// Callback to notify upon changes.
|
||||
FilePathWatcher::Callback callback_;
|
||||
|
||||
// Path we're supposed to watch (passed to callback).
|
||||
FilePath target_;
|
||||
|
||||
// Set to true in the destructor.
|
||||
bool* was_deleted_ptr_ = nullptr;
|
||||
|
||||
// Handle for FindFirstChangeNotification.
|
||||
HANDLE handle_;
|
||||
|
||||
// ObjectWatcher to watch handle_ for events.
|
||||
base::win::ObjectWatcher watcher_;
|
||||
|
||||
// Set to true to watch the sub trees of the specified directory file path.
|
||||
bool recursive_watch_;
|
||||
|
||||
// Keep track of the last modified time of the file. We use nulltime
|
||||
// to represent the file not existing.
|
||||
Time last_modified_;
|
||||
|
||||
// The time at which we processed the first notification with the
|
||||
// |last_modified_| time stamp.
|
||||
Time first_notification_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
|
||||
};
|
||||
|
||||
FilePathWatcherImpl::~FilePathWatcherImpl() {
|
||||
DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence());
|
||||
if (was_deleted_ptr_)
|
||||
*was_deleted_ptr_ = true;
|
||||
}
|
||||
|
||||
bool FilePathWatcherImpl::Watch(const FilePath& path,
|
||||
bool recursive,
|
||||
const FilePathWatcher::Callback& callback) {
|
||||
DCHECK(target_.value().empty()); // Can only watch one path.
|
||||
|
||||
set_task_runner(SequencedTaskRunnerHandle::Get());
|
||||
callback_ = callback;
|
||||
target_ = path;
|
||||
recursive_watch_ = recursive;
|
||||
|
||||
File::Info file_info;
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
if (GetFileInfo(target_, &file_info)) {
|
||||
last_modified_ = file_info.last_modified;
|
||||
first_notification_ = Time::Now();
|
||||
}
|
||||
|
||||
if (!UpdateWatch())
|
||||
return false;
|
||||
|
||||
watcher_.StartWatchingOnce(handle_, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::Cancel() {
|
||||
if (callback_.is_null()) {
|
||||
// Watch was never called, or the |task_runner_| has already quit.
|
||||
set_cancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(task_runner()->RunsTasksInCurrentSequence());
|
||||
set_cancelled();
|
||||
|
||||
if (handle_ != INVALID_HANDLE_VALUE)
|
||||
DestroyWatch();
|
||||
|
||||
callback_.Reset();
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
|
||||
DCHECK(task_runner()->RunsTasksInCurrentSequence());
|
||||
DCHECK_EQ(object, handle_);
|
||||
DCHECK(!was_deleted_ptr_);
|
||||
|
||||
bool was_deleted = false;
|
||||
was_deleted_ptr_ = &was_deleted;
|
||||
|
||||
if (!UpdateWatch()) {
|
||||
callback_.Run(target_, true /* error */);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether the event applies to |target_| and notify the callback.
|
||||
File::Info file_info;
|
||||
bool file_exists = false;
|
||||
{
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
file_exists = GetFileInfo(target_, &file_info);
|
||||
}
|
||||
if (recursive_watch_) {
|
||||
// Only the mtime of |target_| is tracked but in a recursive watch,
|
||||
// some other file or directory may have changed so all notifications
|
||||
// are passed through. It is possible to figure out which file changed
|
||||
// using ReadDirectoryChangesW() instead of FindFirstChangeNotification(),
|
||||
// but that function is quite complicated:
|
||||
// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
|
||||
callback_.Run(target_, false);
|
||||
} else if (file_exists && (last_modified_.is_null() ||
|
||||
last_modified_ != file_info.last_modified)) {
|
||||
last_modified_ = file_info.last_modified;
|
||||
first_notification_ = Time::Now();
|
||||
callback_.Run(target_, false);
|
||||
} else if (file_exists && last_modified_ == file_info.last_modified &&
|
||||
!first_notification_.is_null()) {
|
||||
// The target's last modification time is equal to what's on record. This
|
||||
// means that either an unrelated event occurred, or the target changed
|
||||
// again (file modification times only have a resolution of 1s). Comparing
|
||||
// file modification times against the wall clock is not reliable to find
|
||||
// out whether the change is recent, since this code might just run too
|
||||
// late. Moreover, there's no guarantee that file modification time and wall
|
||||
// clock times come from the same source.
|
||||
//
|
||||
// Instead, the time at which the first notification carrying the current
|
||||
// |last_notified_| time stamp is recorded. Later notifications that find
|
||||
// the same file modification time only need to be forwarded until wall
|
||||
// clock has advanced one second from the initial notification. After that
|
||||
// interval, client code is guaranteed to having seen the current revision
|
||||
// of the file.
|
||||
if (Time::Now() - first_notification_ > TimeDelta::FromSeconds(1)) {
|
||||
// Stop further notifications for this |last_modification_| time stamp.
|
||||
first_notification_ = Time();
|
||||
}
|
||||
callback_.Run(target_, false);
|
||||
} else if (!file_exists && !last_modified_.is_null()) {
|
||||
last_modified_ = Time();
|
||||
callback_.Run(target_, false);
|
||||
}
|
||||
|
||||
// The watch may have been cancelled by the callback.
|
||||
if (!was_deleted) {
|
||||
watcher_.StartWatchingOnce(handle_, this);
|
||||
was_deleted_ptr_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir,
|
||||
bool recursive,
|
||||
HANDLE* handle) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
*handle = FindFirstChangeNotification(
|
||||
dir.value().c_str(), recursive,
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY);
|
||||
if (*handle != INVALID_HANDLE_VALUE) {
|
||||
// Make sure the handle we got points to an existing directory. It seems
|
||||
// that windows sometimes hands out watches to directories that are
|
||||
// about to go away, but doesn't sent notifications if that happens.
|
||||
if (!DirectoryExists(dir)) {
|
||||
FindCloseChangeNotification(*handle);
|
||||
*handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If FindFirstChangeNotification failed because the target directory
|
||||
// doesn't exist, access is denied (happens if the file is already gone but
|
||||
// there are still handles open), or the target is not a directory, try the
|
||||
// immediate parent directory instead.
|
||||
DWORD error_code = GetLastError();
|
||||
if (error_code != ERROR_FILE_NOT_FOUND &&
|
||||
error_code != ERROR_PATH_NOT_FOUND &&
|
||||
error_code != ERROR_ACCESS_DENIED &&
|
||||
error_code != ERROR_SHARING_VIOLATION &&
|
||||
error_code != ERROR_DIRECTORY) {
|
||||
DPLOG(ERROR) << "FindFirstChangeNotification failed for "
|
||||
<< dir.value();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilePathWatcherImpl::UpdateWatch() {
|
||||
if (handle_ != INVALID_HANDLE_VALUE)
|
||||
DestroyWatch();
|
||||
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
|
||||
// Start at the target and walk up the directory chain until we succesfully
|
||||
// create a watch handle in |handle_|. |child_dirs| keeps a stack of child
|
||||
// directories stripped from target, in reverse order.
|
||||
std::vector<FilePath> child_dirs;
|
||||
FilePath watched_path(target_);
|
||||
while (true) {
|
||||
if (!SetupWatchHandle(watched_path, recursive_watch_, &handle_))
|
||||
return false;
|
||||
|
||||
// Break if a valid handle is returned. Try the parent directory otherwise.
|
||||
if (handle_ != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
|
||||
// Abort if we hit the root directory.
|
||||
child_dirs.push_back(watched_path.BaseName());
|
||||
FilePath parent(watched_path.DirName());
|
||||
if (parent == watched_path) {
|
||||
DLOG(ERROR) << "Reached the root directory";
|
||||
return false;
|
||||
}
|
||||
watched_path = parent;
|
||||
}
|
||||
|
||||
// At this point, handle_ is valid. However, the bottom-up search that the
|
||||
// above code performs races against directory creation. So try to walk back
|
||||
// down and see whether any children appeared in the mean time.
|
||||
while (!child_dirs.empty()) {
|
||||
watched_path = watched_path.Append(child_dirs.back());
|
||||
child_dirs.pop_back();
|
||||
HANDLE temp_handle = INVALID_HANDLE_VALUE;
|
||||
if (!SetupWatchHandle(watched_path, recursive_watch_, &temp_handle))
|
||||
return false;
|
||||
if (temp_handle == INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
FindCloseChangeNotification(handle_);
|
||||
handle_ = temp_handle;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilePathWatcherImpl::DestroyWatch() {
|
||||
watcher_.StopWatching();
|
||||
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
FindCloseChangeNotification(handle_);
|
||||
handle_ = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FilePathWatcher::FilePathWatcher() {
|
||||
sequence_checker_.DetachFromSequence();
|
||||
impl_ = std::make_unique<FilePathWatcherImpl>();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
613
TMessagesProj/jni/voip/webrtc/base/files/file_posix.cc
Normal file
613
TMessagesProj/jni/voip/webrtc/base/files/file_posix.cc
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include "base/os_compat_android.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
// Make sure our Whence mappings match the system headers.
|
||||
static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&
|
||||
File::FROM_END == SEEK_END,
|
||||
"whence mapping must match the system headers");
|
||||
|
||||
namespace {
|
||||
|
||||
// NaCl doesn't provide the following system calls, so either simulate them or
|
||||
// wrap them in order to minimize the number of #ifdef's in this file.
|
||||
#if !defined(OS_NACL) && !defined(OS_AIX)
|
||||
bool IsOpenAppend(PlatformFile file) {
|
||||
return (fcntl(file, F_GETFL) & O_APPEND) != 0;
|
||||
}
|
||||
|
||||
int CallFtruncate(PlatformFile file, int64_t length) {
|
||||
return HANDLE_EINTR(ftruncate(file, length));
|
||||
}
|
||||
|
||||
int CallFutimes(PlatformFile file, const struct timeval times[2]) {
|
||||
#ifdef __USE_XOPEN2K8
|
||||
// futimens should be available, but futimes might not be
|
||||
// http://pubs.opengroup.org/onlinepubs/9699919799/
|
||||
|
||||
timespec ts_times[2];
|
||||
ts_times[0].tv_sec = times[0].tv_sec;
|
||||
ts_times[0].tv_nsec = times[0].tv_usec * 1000;
|
||||
ts_times[1].tv_sec = times[1].tv_sec;
|
||||
ts_times[1].tv_nsec = times[1].tv_usec * 1000;
|
||||
|
||||
return futimens(file, ts_times);
|
||||
#else
|
||||
return futimes(file, times);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(OS_FUCHSIA)
|
||||
short FcntlFlockType(base::Optional<File::LockMode> mode) {
|
||||
if (!mode.has_value())
|
||||
return F_UNLCK;
|
||||
switch (mode.value()) {
|
||||
case File::LockMode::kShared:
|
||||
return F_RDLCK;
|
||||
case File::LockMode::kExclusive:
|
||||
return F_WRLCK;
|
||||
}
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
File::Error CallFcntlFlock(PlatformFile file,
|
||||
base::Optional<File::LockMode> mode) {
|
||||
struct flock lock;
|
||||
lock.l_type = FcntlFlockType(std::move(mode));
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 0; // Lock entire file.
|
||||
if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
|
||||
return File::GetLastFileError();
|
||||
return File::FILE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else // defined(OS_NACL) && !defined(OS_AIX)
|
||||
|
||||
bool IsOpenAppend(PlatformFile file) {
|
||||
// NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
|
||||
// standard and always appends if the file is opened with O_APPEND, just
|
||||
// return false here.
|
||||
return false;
|
||||
}
|
||||
|
||||
int CallFtruncate(PlatformFile file, int64_t length) {
|
||||
NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CallFutimes(PlatformFile file, const struct timeval times[2]) {
|
||||
NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
|
||||
return 0;
|
||||
}
|
||||
|
||||
File::Error CallFcntlFlock(PlatformFile file,
|
||||
base::Optional<File::LockMode> mode) {
|
||||
NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
|
||||
return File::FILE_ERROR_INVALID_OPERATION;
|
||||
}
|
||||
#endif // defined(OS_NACL)
|
||||
|
||||
} // namespace
|
||||
|
||||
void File::Info::FromStat(const stat_wrapper_t& stat_info) {
|
||||
is_directory = S_ISDIR(stat_info.st_mode);
|
||||
is_symbolic_link = S_ISLNK(stat_info.st_mode);
|
||||
size = stat_info.st_size;
|
||||
|
||||
// Get last modification time, last access time, and creation time from
|
||||
// |stat_info|.
|
||||
// Note: st_ctime is actually last status change time when the inode was last
|
||||
// updated, which happens on any metadata change. It is not the file's
|
||||
// creation time. However, other than on Mac & iOS where the actual file
|
||||
// creation time is included as st_birthtime, the rest of POSIX platforms have
|
||||
// no portable way to get the creation time.
|
||||
#if defined(OS_LINUX) || defined(OS_FUCHSIA)
|
||||
time_t last_modified_sec = stat_info.st_mtim.tv_sec;
|
||||
int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
|
||||
time_t last_accessed_sec = stat_info.st_atim.tv_sec;
|
||||
int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
|
||||
time_t creation_time_sec = stat_info.st_ctim.tv_sec;
|
||||
int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
|
||||
#elif defined(OS_ANDROID)
|
||||
time_t last_modified_sec = stat_info.st_mtime;
|
||||
int64_t last_modified_nsec = stat_info.st_mtime_nsec;
|
||||
time_t last_accessed_sec = stat_info.st_atime;
|
||||
int64_t last_accessed_nsec = stat_info.st_atime_nsec;
|
||||
time_t creation_time_sec = stat_info.st_ctime;
|
||||
int64_t creation_time_nsec = stat_info.st_ctime_nsec;
|
||||
#elif defined(OS_MACOSX) || defined(OS_IOS)
|
||||
time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
|
||||
int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
|
||||
time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
|
||||
int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
|
||||
time_t creation_time_sec = stat_info.st_birthtimespec.tv_sec;
|
||||
int64_t creation_time_nsec = stat_info.st_birthtimespec.tv_nsec;
|
||||
#elif defined(OS_BSD)
|
||||
time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
|
||||
int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
|
||||
time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
|
||||
int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
|
||||
time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
|
||||
int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
|
||||
#else
|
||||
time_t last_modified_sec = stat_info.st_mtime;
|
||||
int64_t last_modified_nsec = 0;
|
||||
time_t last_accessed_sec = stat_info.st_atime;
|
||||
int64_t last_accessed_nsec = 0;
|
||||
time_t creation_time_sec = stat_info.st_ctime;
|
||||
int64_t creation_time_nsec = 0;
|
||||
#endif
|
||||
|
||||
last_modified =
|
||||
Time::FromTimeT(last_modified_sec) +
|
||||
TimeDelta::FromMicroseconds(last_modified_nsec /
|
||||
Time::kNanosecondsPerMicrosecond);
|
||||
|
||||
last_accessed =
|
||||
Time::FromTimeT(last_accessed_sec) +
|
||||
TimeDelta::FromMicroseconds(last_accessed_nsec /
|
||||
Time::kNanosecondsPerMicrosecond);
|
||||
|
||||
creation_time =
|
||||
Time::FromTimeT(creation_time_sec) +
|
||||
TimeDelta::FromMicroseconds(creation_time_nsec /
|
||||
Time::kNanosecondsPerMicrosecond);
|
||||
}
|
||||
|
||||
bool File::IsValid() const {
|
||||
return file_.is_valid();
|
||||
}
|
||||
|
||||
PlatformFile File::GetPlatformFile() const {
|
||||
return file_.get();
|
||||
}
|
||||
|
||||
PlatformFile File::TakePlatformFile() {
|
||||
return file_.release();
|
||||
}
|
||||
|
||||
void File::Close() {
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
SCOPED_FILE_TRACE("Close");
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
file_.reset();
|
||||
}
|
||||
|
||||
int64_t File::Seek(Whence whence, int64_t offset) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset);
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
static_assert(sizeof(int64_t) == sizeof(off64_t), "off64_t must be 64 bits");
|
||||
return lseek64(file_.get(), static_cast<off64_t>(offset),
|
||||
static_cast<int>(whence));
|
||||
#else
|
||||
static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
|
||||
return lseek(file_.get(), static_cast<off_t>(offset),
|
||||
static_cast<int>(whence));
|
||||
#endif
|
||||
}
|
||||
|
||||
int File::Read(int64_t offset, char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("Read", size);
|
||||
|
||||
int bytes_read = 0;
|
||||
int rv;
|
||||
do {
|
||||
rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read,
|
||||
size - bytes_read, offset + bytes_read));
|
||||
if (rv <= 0)
|
||||
break;
|
||||
|
||||
bytes_read += rv;
|
||||
} while (bytes_read < size);
|
||||
|
||||
return bytes_read ? bytes_read : rv;
|
||||
}
|
||||
|
||||
int File::ReadAtCurrentPos(char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size);
|
||||
|
||||
int bytes_read = 0;
|
||||
int rv;
|
||||
do {
|
||||
rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
|
||||
if (rv <= 0)
|
||||
break;
|
||||
|
||||
bytes_read += rv;
|
||||
} while (bytes_read < size);
|
||||
|
||||
return bytes_read ? bytes_read : rv;
|
||||
}
|
||||
|
||||
int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size);
|
||||
return HANDLE_EINTR(pread(file_.get(), data, size, offset));
|
||||
}
|
||||
|
||||
int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size);
|
||||
return HANDLE_EINTR(read(file_.get(), data, size));
|
||||
}
|
||||
|
||||
int File::Write(int64_t offset, const char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
|
||||
if (IsOpenAppend(file_.get()))
|
||||
return WriteAtCurrentPos(data, size);
|
||||
|
||||
DCHECK(IsValid());
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("Write", size);
|
||||
|
||||
int bytes_written = 0;
|
||||
int rv;
|
||||
do {
|
||||
#if defined(OS_ANDROID)
|
||||
// In case __USE_FILE_OFFSET64 is not used, we need to call pwrite64()
|
||||
// instead of pwrite().
|
||||
static_assert(sizeof(int64_t) == sizeof(off64_t),
|
||||
"off64_t must be 64 bits");
|
||||
rv = HANDLE_EINTR(pwrite64(file_.get(), data + bytes_written,
|
||||
size - bytes_written, offset + bytes_written));
|
||||
#else
|
||||
rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
|
||||
size - bytes_written, offset + bytes_written));
|
||||
#endif
|
||||
if (rv <= 0)
|
||||
break;
|
||||
|
||||
bytes_written += rv;
|
||||
} while (bytes_written < size);
|
||||
|
||||
return bytes_written ? bytes_written : rv;
|
||||
}
|
||||
|
||||
int File::WriteAtCurrentPos(const char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size);
|
||||
|
||||
int bytes_written = 0;
|
||||
int rv;
|
||||
do {
|
||||
rv = HANDLE_EINTR(write(file_.get(), data + bytes_written,
|
||||
size - bytes_written));
|
||||
if (rv <= 0)
|
||||
break;
|
||||
|
||||
bytes_written += rv;
|
||||
} while (bytes_written < size);
|
||||
|
||||
return bytes_written ? bytes_written : rv;
|
||||
}
|
||||
|
||||
int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size);
|
||||
return HANDLE_EINTR(write(file_.get(), data, size));
|
||||
}
|
||||
|
||||
int64_t File::GetLength() {
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE("GetLength");
|
||||
|
||||
stat_wrapper_t file_info;
|
||||
if (Fstat(file_.get(), &file_info))
|
||||
return -1;
|
||||
|
||||
return file_info.st_size;
|
||||
}
|
||||
|
||||
bool File::SetLength(int64_t length) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length);
|
||||
return !CallFtruncate(file_.get(), length);
|
||||
}
|
||||
|
||||
bool File::SetTimes(Time last_access_time, Time last_modified_time) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE("SetTimes");
|
||||
|
||||
timeval times[2];
|
||||
times[0] = last_access_time.ToTimeVal();
|
||||
times[1] = last_modified_time.ToTimeVal();
|
||||
|
||||
return !CallFutimes(file_.get(), times);
|
||||
}
|
||||
|
||||
bool File::GetInfo(Info* info) {
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE("GetInfo");
|
||||
|
||||
stat_wrapper_t file_info;
|
||||
if (Fstat(file_.get(), &file_info))
|
||||
return false;
|
||||
|
||||
info->FromStat(file_info);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(OS_FUCHSIA)
|
||||
File::Error File::Lock(File::LockMode mode) {
|
||||
SCOPED_FILE_TRACE("Lock");
|
||||
return CallFcntlFlock(file_.get(), mode);
|
||||
}
|
||||
|
||||
File::Error File::Unlock() {
|
||||
SCOPED_FILE_TRACE("Unlock");
|
||||
return CallFcntlFlock(file_.get(), base::Optional<File::LockMode>());
|
||||
}
|
||||
#endif
|
||||
|
||||
File File::Duplicate() const {
|
||||
if (!IsValid())
|
||||
return File();
|
||||
|
||||
SCOPED_FILE_TRACE("Duplicate");
|
||||
|
||||
ScopedPlatformFile other_fd(HANDLE_EINTR(dup(GetPlatformFile())));
|
||||
if (!other_fd.is_valid())
|
||||
return File(File::GetLastFileError());
|
||||
|
||||
return File(std::move(other_fd), async());
|
||||
}
|
||||
|
||||
// Static.
|
||||
File::Error File::OSErrorToFileError(int saved_errno) {
|
||||
switch (saved_errno) {
|
||||
case EACCES:
|
||||
case EISDIR:
|
||||
case EROFS:
|
||||
case EPERM:
|
||||
return FILE_ERROR_ACCESS_DENIED;
|
||||
case EBUSY:
|
||||
#if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
|
||||
case ETXTBSY:
|
||||
#endif
|
||||
return FILE_ERROR_IN_USE;
|
||||
case EEXIST:
|
||||
return FILE_ERROR_EXISTS;
|
||||
case EIO:
|
||||
return FILE_ERROR_IO;
|
||||
case ENOENT:
|
||||
return FILE_ERROR_NOT_FOUND;
|
||||
case ENFILE: // fallthrough
|
||||
case EMFILE:
|
||||
return FILE_ERROR_TOO_MANY_OPENED;
|
||||
case ENOMEM:
|
||||
return FILE_ERROR_NO_MEMORY;
|
||||
case ENOSPC:
|
||||
return FILE_ERROR_NO_SPACE;
|
||||
case ENOTDIR:
|
||||
return FILE_ERROR_NOT_A_DIRECTORY;
|
||||
default:
|
||||
#if !defined(OS_NACL) // NaCl build has no metrics code.
|
||||
UmaHistogramSparse("PlatformFile.UnknownErrors.Posix", saved_errno);
|
||||
#endif
|
||||
// This function should only be called for errors.
|
||||
DCHECK_NE(0, saved_errno);
|
||||
return FILE_ERROR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
// NaCl doesn't implement system calls to open files directly.
|
||||
#if !defined(OS_NACL)
|
||||
// TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
|
||||
void File::DoInitialize(const FilePath& path, uint32_t flags) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(!IsValid());
|
||||
|
||||
int open_flags = 0;
|
||||
if (flags & FLAG_CREATE)
|
||||
open_flags = O_CREAT | O_EXCL;
|
||||
|
||||
created_ = false;
|
||||
|
||||
if (flags & FLAG_CREATE_ALWAYS) {
|
||||
DCHECK(!open_flags);
|
||||
DCHECK(flags & FLAG_WRITE);
|
||||
open_flags = O_CREAT | O_TRUNC;
|
||||
}
|
||||
|
||||
if (flags & FLAG_OPEN_TRUNCATED) {
|
||||
DCHECK(!open_flags);
|
||||
DCHECK(flags & FLAG_WRITE);
|
||||
open_flags = O_TRUNC;
|
||||
}
|
||||
|
||||
if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
|
||||
NOTREACHED();
|
||||
errno = EOPNOTSUPP;
|
||||
error_details_ = FILE_ERROR_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & FLAG_WRITE && flags & FLAG_READ) {
|
||||
open_flags |= O_RDWR;
|
||||
} else if (flags & FLAG_WRITE) {
|
||||
open_flags |= O_WRONLY;
|
||||
} else if (!(flags & FLAG_READ) &&
|
||||
!(flags & FLAG_WRITE_ATTRIBUTES) &&
|
||||
!(flags & FLAG_APPEND) &&
|
||||
!(flags & FLAG_OPEN_ALWAYS)) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
if (flags & FLAG_TERMINAL_DEVICE)
|
||||
open_flags |= O_NOCTTY | O_NDELAY;
|
||||
|
||||
if (flags & FLAG_APPEND && flags & FLAG_READ)
|
||||
open_flags |= O_APPEND | O_RDWR;
|
||||
else if (flags & FLAG_APPEND)
|
||||
open_flags |= O_APPEND | O_WRONLY;
|
||||
|
||||
static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
|
||||
|
||||
int mode = S_IRUSR | S_IWUSR;
|
||||
#if defined(OS_CHROMEOS)
|
||||
mode |= S_IRGRP | S_IROTH;
|
||||
#endif
|
||||
|
||||
int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
|
||||
|
||||
if (flags & FLAG_OPEN_ALWAYS) {
|
||||
if (descriptor < 0) {
|
||||
open_flags |= O_CREAT;
|
||||
if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE)
|
||||
open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW
|
||||
|
||||
descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
|
||||
if (descriptor >= 0)
|
||||
created_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor < 0) {
|
||||
error_details_ = File::GetLastFileError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
|
||||
created_ = true;
|
||||
|
||||
if (flags & FLAG_DELETE_ON_CLOSE)
|
||||
unlink(path.value().c_str());
|
||||
|
||||
async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
|
||||
error_details_ = FILE_OK;
|
||||
file_.reset(descriptor);
|
||||
}
|
||||
#endif // !defined(OS_NACL)
|
||||
|
||||
bool File::Flush() {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
SCOPED_FILE_TRACE("Flush");
|
||||
|
||||
#if defined(OS_NACL)
|
||||
NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
|
||||
return true;
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
return !HANDLE_EINTR(fdatasync(file_.get()));
|
||||
#elif defined(OS_MACOSX) || defined(OS_IOS)
|
||||
// On macOS and iOS, fsync() is guaranteed to send the file's data to the
|
||||
// underlying storage device, but may return before the device actually writes
|
||||
// the data to the medium. When used by database systems, this may result in
|
||||
// unexpected data loss.
|
||||
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html
|
||||
if (!HANDLE_EINTR(fcntl(file_.get(), F_FULLFSYNC)))
|
||||
return true;
|
||||
|
||||
// Some filesystms do not support fcntl(F_FULLFSYNC). We handle these cases by
|
||||
// falling back to fsync(). Unfortunately, lack of F_FULLFSYNC support results
|
||||
// in various error codes, so we cannot use the error code as a definitive
|
||||
// indicator that F_FULLFSYNC was not supported. So, if fcntl() errors out for
|
||||
// any reason, we may end up making an unnecessary system call.
|
||||
//
|
||||
// See the CL description at https://crrev.com/c/1400159 for details.
|
||||
return !HANDLE_EINTR(fsync(file_.get()));
|
||||
#else
|
||||
return !HANDLE_EINTR(fsync(file_.get()));
|
||||
#endif
|
||||
}
|
||||
|
||||
void File::SetPlatformFile(PlatformFile file) {
|
||||
DCHECK(!file_.is_valid());
|
||||
file_.reset(file);
|
||||
}
|
||||
|
||||
// static
|
||||
File::Error File::GetLastFileError() {
|
||||
return base::File::OSErrorToFileError(errno);
|
||||
}
|
||||
|
||||
#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
|
||||
defined(OS_FUCHSIA) || (defined(OS_ANDROID) && __ANDROID_API__ < 21)
|
||||
int File::Stat(const char* path, stat_wrapper_t* sb) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
return stat(path, sb);
|
||||
}
|
||||
int File::Fstat(int fd, stat_wrapper_t* sb) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
return fstat(fd, sb);
|
||||
}
|
||||
int File::Lstat(const char* path, stat_wrapper_t* sb) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
return lstat(path, sb);
|
||||
}
|
||||
#else
|
||||
int File::Stat(const char* path, stat_wrapper_t* sb) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
return stat64(path, sb);
|
||||
}
|
||||
int File::Fstat(int fd, stat_wrapper_t* sb) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
return fstat64(fd, sb);
|
||||
}
|
||||
int File::Lstat(const char* path, stat_wrapper_t* sb) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
return lstat64(path, sb);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
358
TMessagesProj/jni/voip/webrtc/base/files/file_proxy.cc
Normal file
358
TMessagesProj/jni/voip/webrtc/base/files/file_proxy.cc
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_proxy.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/location.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/task_runner.h"
|
||||
#include "base/task_runner_util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void FileDeleter(base::File file) {
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace base {
|
||||
|
||||
class FileHelper {
|
||||
public:
|
||||
FileHelper(FileProxy* proxy, File file)
|
||||
: file_(std::move(file)),
|
||||
error_(File::FILE_ERROR_FAILED),
|
||||
task_runner_(proxy->task_runner()),
|
||||
proxy_(AsWeakPtr(proxy)) {
|
||||
}
|
||||
|
||||
void PassFile() {
|
||||
if (proxy_)
|
||||
proxy_->SetFile(std::move(file_));
|
||||
else if (file_.IsValid())
|
||||
task_runner_->PostTask(FROM_HERE,
|
||||
BindOnce(&FileDeleter, std::move(file_)));
|
||||
}
|
||||
|
||||
protected:
|
||||
File file_;
|
||||
File::Error error_;
|
||||
|
||||
private:
|
||||
scoped_refptr<TaskRunner> task_runner_;
|
||||
WeakPtr<FileProxy> proxy_;
|
||||
DISALLOW_COPY_AND_ASSIGN(FileHelper);
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
class GenericFileHelper : public FileHelper {
|
||||
public:
|
||||
GenericFileHelper(FileProxy* proxy, File file)
|
||||
: FileHelper(proxy, std::move(file)) {
|
||||
}
|
||||
|
||||
void Close() {
|
||||
file_.Close();
|
||||
error_ = File::FILE_OK;
|
||||
}
|
||||
|
||||
void SetTimes(Time last_access_time, Time last_modified_time) {
|
||||
bool rv = file_.SetTimes(last_access_time, last_modified_time);
|
||||
error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED;
|
||||
}
|
||||
|
||||
void SetLength(int64_t length) {
|
||||
if (file_.SetLength(length))
|
||||
error_ = File::FILE_OK;
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
if (file_.Flush())
|
||||
error_ = File::FILE_OK;
|
||||
}
|
||||
|
||||
void Reply(FileProxy::StatusCallback callback) {
|
||||
PassFile();
|
||||
if (!callback.is_null())
|
||||
std::move(callback).Run(error_);
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(GenericFileHelper);
|
||||
};
|
||||
|
||||
class CreateOrOpenHelper : public FileHelper {
|
||||
public:
|
||||
CreateOrOpenHelper(FileProxy* proxy, File file)
|
||||
: FileHelper(proxy, std::move(file)) {
|
||||
}
|
||||
|
||||
void RunWork(const FilePath& file_path, int file_flags) {
|
||||
file_.Initialize(file_path, file_flags);
|
||||
error_ = file_.IsValid() ? File::FILE_OK : file_.error_details();
|
||||
}
|
||||
|
||||
void Reply(FileProxy::StatusCallback callback) {
|
||||
DCHECK(!callback.is_null());
|
||||
PassFile();
|
||||
std::move(callback).Run(error_);
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper);
|
||||
};
|
||||
|
||||
class CreateTemporaryHelper : public FileHelper {
|
||||
public:
|
||||
CreateTemporaryHelper(FileProxy* proxy, File file)
|
||||
: FileHelper(proxy, std::move(file)) {
|
||||
}
|
||||
|
||||
void RunWork(uint32_t additional_file_flags) {
|
||||
// TODO(darin): file_util should have a variant of CreateTemporaryFile
|
||||
// that returns a FilePath and a File.
|
||||
if (!CreateTemporaryFile(&file_path_)) {
|
||||
// TODO(davidben): base::CreateTemporaryFile should preserve the error
|
||||
// code.
|
||||
error_ = File::FILE_ERROR_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t file_flags = File::FLAG_WRITE | File::FLAG_TEMPORARY |
|
||||
File::FLAG_CREATE_ALWAYS | additional_file_flags;
|
||||
|
||||
file_.Initialize(file_path_, file_flags);
|
||||
if (file_.IsValid()) {
|
||||
error_ = File::FILE_OK;
|
||||
} else {
|
||||
error_ = file_.error_details();
|
||||
DeleteFile(file_path_, false);
|
||||
file_path_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Reply(FileProxy::CreateTemporaryCallback callback) {
|
||||
DCHECK(!callback.is_null());
|
||||
PassFile();
|
||||
std::move(callback).Run(error_, file_path_);
|
||||
}
|
||||
|
||||
private:
|
||||
FilePath file_path_;
|
||||
DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper);
|
||||
};
|
||||
|
||||
class GetInfoHelper : public FileHelper {
|
||||
public:
|
||||
GetInfoHelper(FileProxy* proxy, File file)
|
||||
: FileHelper(proxy, std::move(file)) {
|
||||
}
|
||||
|
||||
void RunWork() {
|
||||
if (file_.GetInfo(&file_info_))
|
||||
error_ = File::FILE_OK;
|
||||
}
|
||||
|
||||
void Reply(FileProxy::GetFileInfoCallback callback) {
|
||||
PassFile();
|
||||
DCHECK(!callback.is_null());
|
||||
std::move(callback).Run(error_, file_info_);
|
||||
}
|
||||
|
||||
private:
|
||||
File::Info file_info_;
|
||||
DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
|
||||
};
|
||||
|
||||
class ReadHelper : public FileHelper {
|
||||
public:
|
||||
ReadHelper(FileProxy* proxy, File file, int bytes_to_read)
|
||||
: FileHelper(proxy, std::move(file)),
|
||||
buffer_(new char[bytes_to_read]),
|
||||
bytes_to_read_(bytes_to_read),
|
||||
bytes_read_(0) {
|
||||
}
|
||||
|
||||
void RunWork(int64_t offset) {
|
||||
bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_);
|
||||
error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
|
||||
}
|
||||
|
||||
void Reply(FileProxy::ReadCallback callback) {
|
||||
PassFile();
|
||||
DCHECK(!callback.is_null());
|
||||
std::move(callback).Run(error_, buffer_.get(), bytes_read_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> buffer_;
|
||||
int bytes_to_read_;
|
||||
int bytes_read_;
|
||||
DISALLOW_COPY_AND_ASSIGN(ReadHelper);
|
||||
};
|
||||
|
||||
class WriteHelper : public FileHelper {
|
||||
public:
|
||||
WriteHelper(FileProxy* proxy,
|
||||
File file,
|
||||
const char* buffer, int bytes_to_write)
|
||||
: FileHelper(proxy, std::move(file)),
|
||||
buffer_(new char[bytes_to_write]),
|
||||
bytes_to_write_(bytes_to_write),
|
||||
bytes_written_(0) {
|
||||
memcpy(buffer_.get(), buffer, bytes_to_write);
|
||||
}
|
||||
|
||||
void RunWork(int64_t offset) {
|
||||
bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_);
|
||||
error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
|
||||
}
|
||||
|
||||
void Reply(FileProxy::WriteCallback callback) {
|
||||
PassFile();
|
||||
if (!callback.is_null())
|
||||
std::move(callback).Run(error_, bytes_written_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> buffer_;
|
||||
int bytes_to_write_;
|
||||
int bytes_written_;
|
||||
DISALLOW_COPY_AND_ASSIGN(WriteHelper);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {
|
||||
}
|
||||
|
||||
FileProxy::~FileProxy() {
|
||||
if (file_.IsValid())
|
||||
task_runner_->PostTask(FROM_HERE, BindOnce(&FileDeleter, std::move(file_)));
|
||||
}
|
||||
|
||||
bool FileProxy::CreateOrOpen(const FilePath& file_path,
|
||||
uint32_t file_flags,
|
||||
StatusCallback callback) {
|
||||
DCHECK(!file_.IsValid());
|
||||
CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File());
|
||||
return task_runner_->PostTaskAndReply(
|
||||
FROM_HERE,
|
||||
BindOnce(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path,
|
||||
file_flags),
|
||||
BindOnce(&CreateOrOpenHelper::Reply, Owned(helper), std::move(callback)));
|
||||
}
|
||||
|
||||
bool FileProxy::CreateTemporary(uint32_t additional_file_flags,
|
||||
CreateTemporaryCallback callback) {
|
||||
DCHECK(!file_.IsValid());
|
||||
CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File());
|
||||
return task_runner_->PostTaskAndReply(
|
||||
FROM_HERE,
|
||||
BindOnce(&CreateTemporaryHelper::RunWork, Unretained(helper),
|
||||
additional_file_flags),
|
||||
BindOnce(&CreateTemporaryHelper::Reply, Owned(helper),
|
||||
std::move(callback)));
|
||||
}
|
||||
|
||||
bool FileProxy::IsValid() const {
|
||||
return file_.IsValid();
|
||||
}
|
||||
|
||||
void FileProxy::SetFile(File file) {
|
||||
DCHECK(!file_.IsValid());
|
||||
file_ = std::move(file);
|
||||
}
|
||||
|
||||
File FileProxy::TakeFile() {
|
||||
return std::move(file_);
|
||||
}
|
||||
|
||||
File FileProxy::DuplicateFile() {
|
||||
return file_.Duplicate();
|
||||
}
|
||||
|
||||
PlatformFile FileProxy::GetPlatformFile() const {
|
||||
return file_.GetPlatformFile();
|
||||
}
|
||||
|
||||
bool FileProxy::Close(StatusCallback callback) {
|
||||
DCHECK(file_.IsValid());
|
||||
GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
|
||||
return task_runner_->PostTaskAndReply(
|
||||
FROM_HERE, BindOnce(&GenericFileHelper::Close, Unretained(helper)),
|
||||
BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
|
||||
}
|
||||
|
||||
bool FileProxy::GetInfo(GetFileInfoCallback callback) {
|
||||
DCHECK(file_.IsValid());
|
||||
GetInfoHelper* helper = new GetInfoHelper(this, std::move(file_));
|
||||
return task_runner_->PostTaskAndReply(
|
||||
FROM_HERE, BindOnce(&GetInfoHelper::RunWork, Unretained(helper)),
|
||||
BindOnce(&GetInfoHelper::Reply, Owned(helper), std::move(callback)));
|
||||
}
|
||||
|
||||
bool FileProxy::Read(int64_t offset, int bytes_to_read, ReadCallback callback) {
|
||||
DCHECK(file_.IsValid());
|
||||
if (bytes_to_read < 0)
|
||||
return false;
|
||||
|
||||
ReadHelper* helper = new ReadHelper(this, std::move(file_), bytes_to_read);
|
||||
return task_runner_->PostTaskAndReply(
|
||||
FROM_HERE, BindOnce(&ReadHelper::RunWork, Unretained(helper), offset),
|
||||
BindOnce(&ReadHelper::Reply, Owned(helper), std::move(callback)));
|
||||
}
|
||||
|
||||
bool FileProxy::Write(int64_t offset,
|
||||
const char* buffer,
|
||||
int bytes_to_write,
|
||||
WriteCallback callback) {
|
||||
DCHECK(file_.IsValid());
|
||||
if (bytes_to_write <= 0 || buffer == nullptr)
|
||||
return false;
|
||||
|
||||
WriteHelper* helper =
|
||||
new WriteHelper(this, std::move(file_), buffer, bytes_to_write);
|
||||
return task_runner_->PostTaskAndReply(
|
||||
FROM_HERE, BindOnce(&WriteHelper::RunWork, Unretained(helper), offset),
|
||||
BindOnce(&WriteHelper::Reply, Owned(helper), std::move(callback)));
|
||||
}
|
||||
|
||||
bool FileProxy::SetTimes(Time last_access_time,
|
||||
Time last_modified_time,
|
||||
StatusCallback callback) {
|
||||
DCHECK(file_.IsValid());
|
||||
GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
|
||||
return task_runner_->PostTaskAndReply(
|
||||
FROM_HERE,
|
||||
BindOnce(&GenericFileHelper::SetTimes, Unretained(helper),
|
||||
last_access_time, last_modified_time),
|
||||
BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
|
||||
}
|
||||
|
||||
bool FileProxy::SetLength(int64_t length, StatusCallback callback) {
|
||||
DCHECK(file_.IsValid());
|
||||
GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
|
||||
return task_runner_->PostTaskAndReply(
|
||||
FROM_HERE,
|
||||
BindOnce(&GenericFileHelper::SetLength, Unretained(helper), length),
|
||||
BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
|
||||
}
|
||||
|
||||
bool FileProxy::Flush(StatusCallback callback) {
|
||||
DCHECK(file_.IsValid());
|
||||
GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
|
||||
return task_runner_->PostTaskAndReply(
|
||||
FROM_HERE, BindOnce(&GenericFileHelper::Flush, Unretained(helper)),
|
||||
BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
142
TMessagesProj/jni/voip/webrtc/base/files/file_proxy.h
Normal file
142
TMessagesProj/jni/voip/webrtc/base/files/file_proxy.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_FILE_PROXY_H_
|
||||
#define BASE_FILES_FILE_PROXY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/callback_forward.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class TaskRunner;
|
||||
class Time;
|
||||
|
||||
// This class provides asynchronous access to a File. All methods follow the
|
||||
// same rules of the equivalent File method, as they are implemented by bouncing
|
||||
// the operation to File using a TaskRunner.
|
||||
//
|
||||
// This class performs automatic proxying to close the underlying file at
|
||||
// destruction.
|
||||
//
|
||||
// The TaskRunner is in charge of any sequencing of the operations, but a single
|
||||
// operation can be proxied at a time, regardless of the use of a callback.
|
||||
// In other words, having a sequence like
|
||||
//
|
||||
// proxy.Write(...);
|
||||
// proxy.Write(...);
|
||||
//
|
||||
// means the second Write will always fail.
|
||||
class BASE_EXPORT FileProxy : public SupportsWeakPtr<FileProxy> {
|
||||
public:
|
||||
// This callback is used by methods that report only an error code. It is
|
||||
// valid to pass a null callback to some functions that takes a
|
||||
// StatusCallback, in which case the operation will complete silently.
|
||||
using StatusCallback = OnceCallback<void(File::Error)>;
|
||||
using CreateTemporaryCallback =
|
||||
OnceCallback<void(File::Error, const FilePath&)>;
|
||||
using GetFileInfoCallback =
|
||||
OnceCallback<void(File::Error, const File::Info&)>;
|
||||
using ReadCallback =
|
||||
OnceCallback<void(File::Error, const char* data, int bytes_read)>;
|
||||
using WriteCallback = OnceCallback<void(File::Error, int bytes_written)>;
|
||||
|
||||
FileProxy();
|
||||
explicit FileProxy(TaskRunner* task_runner);
|
||||
~FileProxy();
|
||||
|
||||
// Creates or opens a file with the given flags. It is invalid to pass a null
|
||||
// callback. If File::FLAG_CREATE is set in |file_flags| it always tries to
|
||||
// create a new file at the given |file_path| and fails if the file already
|
||||
// exists.
|
||||
//
|
||||
// This returns false if task posting to |task_runner| has failed.
|
||||
bool CreateOrOpen(const FilePath& file_path,
|
||||
uint32_t file_flags,
|
||||
StatusCallback callback);
|
||||
|
||||
// Creates a temporary file for writing. The path and an open file are
|
||||
// returned. It is invalid to pass a null callback. The additional file flags
|
||||
// will be added on top of the default file flags which are:
|
||||
// File::FLAG_CREATE_ALWAYS
|
||||
// File::FLAG_WRITE
|
||||
// File::FLAG_TEMPORARY.
|
||||
//
|
||||
// This returns false if task posting to |task_runner| has failed.
|
||||
bool CreateTemporary(uint32_t additional_file_flags,
|
||||
CreateTemporaryCallback callback);
|
||||
|
||||
// Returns true if the underlying |file_| is valid.
|
||||
bool IsValid() const;
|
||||
|
||||
// Returns true if a new file was created (or an old one truncated to zero
|
||||
// length to simulate a new file), and false otherwise.
|
||||
bool created() const { return file_.created(); }
|
||||
|
||||
// Claims ownership of |file|. It is an error to call this method when
|
||||
// IsValid() returns true.
|
||||
void SetFile(File file);
|
||||
|
||||
File TakeFile();
|
||||
|
||||
// Returns a new File object that is a duplicate of the underlying |file_|.
|
||||
// See the comment at File::Duplicate for caveats.
|
||||
File DuplicateFile();
|
||||
|
||||
PlatformFile GetPlatformFile() const;
|
||||
|
||||
// Proxies File::Close. The callback can be null.
|
||||
// This returns false if task posting to |task_runner| has failed.
|
||||
bool Close(StatusCallback callback);
|
||||
|
||||
// Proxies File::GetInfo. The callback can't be null.
|
||||
// This returns false if task posting to |task_runner| has failed.
|
||||
bool GetInfo(GetFileInfoCallback callback);
|
||||
|
||||
// Proxies File::Read. The callback can't be null.
|
||||
// This returns false if |bytes_to_read| is less than zero, or
|
||||
// if task posting to |task_runner| has failed.
|
||||
bool Read(int64_t offset, int bytes_to_read, ReadCallback callback);
|
||||
|
||||
// Proxies File::Write. The callback can be null.
|
||||
// This returns false if |bytes_to_write| is less than or equal to zero,
|
||||
// if |buffer| is NULL, or if task posting to |task_runner| has failed.
|
||||
bool Write(int64_t offset,
|
||||
const char* buffer,
|
||||
int bytes_to_write,
|
||||
WriteCallback callback);
|
||||
|
||||
// Proxies File::SetTimes. The callback can be null.
|
||||
// This returns false if task posting to |task_runner| has failed.
|
||||
bool SetTimes(Time last_access_time,
|
||||
Time last_modified_time,
|
||||
StatusCallback callback);
|
||||
|
||||
// Proxies File::SetLength. The callback can be null.
|
||||
// This returns false if task posting to |task_runner| has failed.
|
||||
bool SetLength(int64_t length, StatusCallback callback);
|
||||
|
||||
// Proxies File::Flush. The callback can be null.
|
||||
// This returns false if task posting to |task_runner| has failed.
|
||||
bool Flush(StatusCallback callback);
|
||||
|
||||
private:
|
||||
friend class FileHelper;
|
||||
TaskRunner* task_runner() { return task_runner_.get(); }
|
||||
|
||||
scoped_refptr<TaskRunner> task_runner_;
|
||||
File file_;
|
||||
DISALLOW_COPY_AND_ASSIGN(FileProxy);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_FILE_PROXY_H_
|
||||
65
TMessagesProj/jni/voip/webrtc/base/files/file_tracing.cc
Normal file
65
TMessagesProj/jni/voip/webrtc/base/files/file_tracing.cc
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_tracing.h"
|
||||
|
||||
#include "base/atomicops.h"
|
||||
#include "base/files/file.h"
|
||||
|
||||
using base::subtle::AtomicWord;
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
AtomicWord g_provider;
|
||||
}
|
||||
|
||||
FileTracing::Provider* GetProvider() {
|
||||
AtomicWord provider = base::subtle::Acquire_Load(&g_provider);
|
||||
return reinterpret_cast<FileTracing::Provider*>(provider);
|
||||
}
|
||||
|
||||
// static
|
||||
bool FileTracing::IsCategoryEnabled() {
|
||||
FileTracing::Provider* provider = GetProvider();
|
||||
return provider && provider->FileTracingCategoryIsEnabled();
|
||||
}
|
||||
|
||||
// static
|
||||
void FileTracing::SetProvider(FileTracing::Provider* provider) {
|
||||
base::subtle::Release_Store(&g_provider,
|
||||
reinterpret_cast<AtomicWord>(provider));
|
||||
}
|
||||
|
||||
FileTracing::ScopedEnabler::ScopedEnabler() {
|
||||
FileTracing::Provider* provider = GetProvider();
|
||||
if (provider)
|
||||
provider->FileTracingEnable(this);
|
||||
}
|
||||
|
||||
FileTracing::ScopedEnabler::~ScopedEnabler() {
|
||||
FileTracing::Provider* provider = GetProvider();
|
||||
if (provider)
|
||||
provider->FileTracingDisable(this);
|
||||
}
|
||||
|
||||
FileTracing::ScopedTrace::ScopedTrace() : id_(nullptr) {}
|
||||
|
||||
FileTracing::ScopedTrace::~ScopedTrace() {
|
||||
if (id_) {
|
||||
FileTracing::Provider* provider = GetProvider();
|
||||
if (provider)
|
||||
provider->FileTracingEventEnd(name_, id_);
|
||||
}
|
||||
}
|
||||
|
||||
void FileTracing::ScopedTrace::Initialize(const char* name,
|
||||
const File* file,
|
||||
int64_t size) {
|
||||
id_ = &file->trace_enabler_;
|
||||
name_ = name;
|
||||
GetProvider()->FileTracingEventBegin(name_, id_, file->tracing_path_, size);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
95
TMessagesProj/jni/voip/webrtc/base/files/file_tracing.h
Normal file
95
TMessagesProj/jni/voip/webrtc/base/files/file_tracing.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_FILE_TRACING_H_
|
||||
#define BASE_FILES_FILE_TRACING_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/macros.h"
|
||||
|
||||
#define FILE_TRACING_PREFIX "File"
|
||||
|
||||
#define SCOPED_FILE_TRACE_WITH_SIZE(name, size) \
|
||||
FileTracing::ScopedTrace scoped_file_trace; \
|
||||
if (FileTracing::IsCategoryEnabled()) \
|
||||
scoped_file_trace.Initialize(FILE_TRACING_PREFIX "::" name, this, size)
|
||||
|
||||
#define SCOPED_FILE_TRACE(name) SCOPED_FILE_TRACE_WITH_SIZE(name, 0)
|
||||
|
||||
namespace base {
|
||||
|
||||
class File;
|
||||
class FilePath;
|
||||
|
||||
class BASE_EXPORT FileTracing {
|
||||
public:
|
||||
// Whether the file tracing category is enabled.
|
||||
static bool IsCategoryEnabled();
|
||||
|
||||
class Provider {
|
||||
public:
|
||||
virtual ~Provider() = default;
|
||||
|
||||
// Whether the file tracing category is currently enabled.
|
||||
virtual bool FileTracingCategoryIsEnabled() const = 0;
|
||||
|
||||
// Enables file tracing for |id|. Must be called before recording events.
|
||||
virtual void FileTracingEnable(const void* id) = 0;
|
||||
|
||||
// Disables file tracing for |id|.
|
||||
virtual void FileTracingDisable(const void* id) = 0;
|
||||
|
||||
// Begins an event for |id| with |name|. |path| tells where in the directory
|
||||
// structure the event is happening (and may be blank). |size| is the number
|
||||
// of bytes involved in the event.
|
||||
virtual void FileTracingEventBegin(const char* name,
|
||||
const void* id,
|
||||
const FilePath& path,
|
||||
int64_t size) = 0;
|
||||
|
||||
// Ends an event for |id| with |name|.
|
||||
virtual void FileTracingEventEnd(const char* name, const void* id) = 0;
|
||||
};
|
||||
|
||||
// Sets a global file tracing provider to query categories and record events.
|
||||
static void SetProvider(Provider* provider);
|
||||
|
||||
// Enables file tracing while in scope.
|
||||
class ScopedEnabler {
|
||||
public:
|
||||
ScopedEnabler();
|
||||
~ScopedEnabler();
|
||||
};
|
||||
|
||||
class ScopedTrace {
|
||||
public:
|
||||
ScopedTrace();
|
||||
~ScopedTrace();
|
||||
|
||||
// Called only if the tracing category is enabled. |name| is the name of the
|
||||
// event to trace (e.g. "Read", "Write") and must have an application
|
||||
// lifetime (e.g. static or literal). |file| is the file being traced; must
|
||||
// outlive this class. |size| is the size (in bytes) of this event.
|
||||
void Initialize(const char* name, const File* file, int64_t size);
|
||||
|
||||
private:
|
||||
// The ID of this trace. Based on the |file| passed to |Initialize()|. Must
|
||||
// outlive this class.
|
||||
const void* id_;
|
||||
|
||||
// The name of the event to trace (e.g. "Read", "Write"). Prefixed with
|
||||
// "File".
|
||||
const char* name_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedTrace);
|
||||
};
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FileTracing);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_FILE_TRACING_H_
|
||||
337
TMessagesProj/jni/voip/webrtc/base/files/file_util.cc
Normal file
337
TMessagesProj/jni/voip/webrtc/base/files/file_util.cc
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <io.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#include "base/files/file_enumerator.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
#if !defined(OS_NACL_NONSFI)
|
||||
int64_t ComputeDirectorySize(const FilePath& root_path) {
|
||||
int64_t running_size = 0;
|
||||
FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
|
||||
while (!file_iter.Next().empty())
|
||||
running_size += file_iter.GetInfo().GetSize();
|
||||
return running_size;
|
||||
}
|
||||
|
||||
bool Move(const FilePath& from_path, const FilePath& to_path) {
|
||||
if (from_path.ReferencesParent() || to_path.ReferencesParent())
|
||||
return false;
|
||||
return internal::MoveUnsafe(from_path, to_path);
|
||||
}
|
||||
|
||||
bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
|
||||
// We open the file in binary format even if they are text files because
|
||||
// we are just comparing that bytes are exactly same in both files and not
|
||||
// doing anything smart with text formatting.
|
||||
#if defined(OS_WIN)
|
||||
std::ifstream file1(filename1.value().c_str(),
|
||||
std::ios::in | std::ios::binary);
|
||||
std::ifstream file2(filename2.value().c_str(),
|
||||
std::ios::in | std::ios::binary);
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
std::ifstream file1(filename1.value(), std::ios::in | std::ios::binary);
|
||||
std::ifstream file2(filename2.value(), std::ios::in | std::ios::binary);
|
||||
#endif // OS_WIN
|
||||
|
||||
// Even if both files aren't openable (and thus, in some sense, "equal"),
|
||||
// any unusable file yields a result of "false".
|
||||
if (!file1.is_open() || !file2.is_open())
|
||||
return false;
|
||||
|
||||
const int BUFFER_SIZE = 2056;
|
||||
char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
|
||||
do {
|
||||
file1.read(buffer1, BUFFER_SIZE);
|
||||
file2.read(buffer2, BUFFER_SIZE);
|
||||
|
||||
if ((file1.eof() != file2.eof()) ||
|
||||
(file1.gcount() != file2.gcount()) ||
|
||||
(memcmp(buffer1, buffer2, static_cast<size_t>(file1.gcount())))) {
|
||||
file1.close();
|
||||
file2.close();
|
||||
return false;
|
||||
}
|
||||
} while (!file1.eof() || !file2.eof());
|
||||
|
||||
file1.close();
|
||||
file2.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
|
||||
#if defined(OS_WIN)
|
||||
std::ifstream file1(filename1.value().c_str(), std::ios::in);
|
||||
std::ifstream file2(filename2.value().c_str(), std::ios::in);
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
std::ifstream file1(filename1.value(), std::ios::in);
|
||||
std::ifstream file2(filename2.value(), std::ios::in);
|
||||
#endif // OS_WIN
|
||||
|
||||
// Even if both files aren't openable (and thus, in some sense, "equal"),
|
||||
// any unusable file yields a result of "false".
|
||||
if (!file1.is_open() || !file2.is_open())
|
||||
return false;
|
||||
|
||||
do {
|
||||
std::string line1, line2;
|
||||
getline(file1, line1);
|
||||
getline(file2, line2);
|
||||
|
||||
// Check for mismatched EOF states, or any error state.
|
||||
if ((file1.eof() != file2.eof()) ||
|
||||
file1.bad() || file2.bad()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Trim all '\r' and '\n' characters from the end of the line.
|
||||
std::string::size_type end1 = line1.find_last_not_of("\r\n");
|
||||
if (end1 == std::string::npos)
|
||||
line1.clear();
|
||||
else if (end1 + 1 < line1.length())
|
||||
line1.erase(end1 + 1);
|
||||
|
||||
std::string::size_type end2 = line2.find_last_not_of("\r\n");
|
||||
if (end2 == std::string::npos)
|
||||
line2.clear();
|
||||
else if (end2 + 1 < line2.length())
|
||||
line2.erase(end2 + 1);
|
||||
|
||||
if (line1 != line2)
|
||||
return false;
|
||||
} while (!file1.eof() || !file2.eof());
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // !defined(OS_NACL_NONSFI)
|
||||
|
||||
bool ReadFileToStringWithMaxSize(const FilePath& path,
|
||||
std::string* contents,
|
||||
size_t max_size) {
|
||||
if (contents)
|
||||
contents->clear();
|
||||
if (path.ReferencesParent())
|
||||
return false;
|
||||
FILE* file = OpenFile(path, "rb");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Many files supplied in |path| have incorrect size (proc files etc).
|
||||
// Hence, the file is read sequentially as opposed to a one-shot read, using
|
||||
// file size as a hint for chunk size if available.
|
||||
constexpr int64_t kDefaultChunkSize = 1 << 16;
|
||||
int64_t chunk_size;
|
||||
#if !defined(OS_NACL_NONSFI)
|
||||
if (!GetFileSize(path, &chunk_size) || chunk_size <= 0)
|
||||
chunk_size = kDefaultChunkSize - 1;
|
||||
// We need to attempt to read at EOF for feof flag to be set so here we
|
||||
// use |chunk_size| + 1.
|
||||
chunk_size = std::min<uint64_t>(chunk_size, max_size) + 1;
|
||||
#else
|
||||
chunk_size = kDefaultChunkSize;
|
||||
#endif // !defined(OS_NACL_NONSFI)
|
||||
size_t bytes_read_this_pass;
|
||||
size_t bytes_read_so_far = 0;
|
||||
bool read_status = true;
|
||||
std::string local_contents;
|
||||
local_contents.resize(chunk_size);
|
||||
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
while ((bytes_read_this_pass = fread(&local_contents[bytes_read_so_far], 1,
|
||||
chunk_size, file)) > 0) {
|
||||
if ((max_size - bytes_read_so_far) < bytes_read_this_pass) {
|
||||
// Read more than max_size bytes, bail out.
|
||||
bytes_read_so_far = max_size;
|
||||
read_status = false;
|
||||
break;
|
||||
}
|
||||
// In case EOF was not reached, iterate again but revert to the default
|
||||
// chunk size.
|
||||
if (bytes_read_so_far == 0)
|
||||
chunk_size = kDefaultChunkSize;
|
||||
|
||||
bytes_read_so_far += bytes_read_this_pass;
|
||||
// Last fread syscall (after EOF) can be avoided via feof, which is just a
|
||||
// flag check.
|
||||
if (feof(file))
|
||||
break;
|
||||
local_contents.resize(bytes_read_so_far + chunk_size);
|
||||
}
|
||||
read_status = read_status && !ferror(file);
|
||||
CloseFile(file);
|
||||
if (contents) {
|
||||
contents->swap(local_contents);
|
||||
contents->resize(bytes_read_so_far);
|
||||
}
|
||||
|
||||
return read_status;
|
||||
}
|
||||
|
||||
bool ReadFileToString(const FilePath& path, std::string* contents) {
|
||||
return ReadFileToStringWithMaxSize(path, contents,
|
||||
std::numeric_limits<size_t>::max());
|
||||
}
|
||||
|
||||
#if !defined(OS_NACL_NONSFI)
|
||||
bool IsDirectoryEmpty(const FilePath& dir_path) {
|
||||
FileEnumerator files(dir_path, false,
|
||||
FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
|
||||
if (files.Next().empty())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* CreateAndOpenTemporaryFile(FilePath* path) {
|
||||
FilePath directory;
|
||||
if (!GetTempDir(&directory))
|
||||
return nullptr;
|
||||
|
||||
return CreateAndOpenTemporaryFileInDir(directory, path);
|
||||
}
|
||||
|
||||
bool CreateDirectory(const FilePath& full_path) {
|
||||
return CreateDirectoryAndGetError(full_path, nullptr);
|
||||
}
|
||||
|
||||
bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
|
||||
File::Info info;
|
||||
if (!GetFileInfo(file_path, &info))
|
||||
return false;
|
||||
*file_size = info.size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TouchFile(const FilePath& path,
|
||||
const Time& last_accessed,
|
||||
const Time& last_modified) {
|
||||
int flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
|
||||
if (DirectoryExists(path))
|
||||
flags |= File::FLAG_BACKUP_SEMANTICS;
|
||||
#elif defined(OS_FUCHSIA)
|
||||
// On Fuchsia, we need O_RDONLY for directories, or O_WRONLY for files.
|
||||
// TODO(https://crbug.com/947802): Find a cleaner workaround for this.
|
||||
flags |= (DirectoryExists(path) ? File::FLAG_READ : File::FLAG_WRITE);
|
||||
#endif
|
||||
|
||||
File file(path, flags);
|
||||
if (!file.IsValid())
|
||||
return false;
|
||||
|
||||
return file.SetTimes(last_accessed, last_modified);
|
||||
}
|
||||
#endif // !defined(OS_NACL_NONSFI)
|
||||
|
||||
bool CloseFile(FILE* file) {
|
||||
if (file == nullptr)
|
||||
return true;
|
||||
return fclose(file) == 0;
|
||||
}
|
||||
|
||||
#if !defined(OS_NACL_NONSFI)
|
||||
bool TruncateFile(FILE* file) {
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
long current_offset = ftell(file);
|
||||
if (current_offset == -1)
|
||||
return false;
|
||||
#if defined(OS_WIN)
|
||||
int fd = _fileno(file);
|
||||
if (_chsize(fd, current_offset) != 0)
|
||||
return false;
|
||||
#else
|
||||
int fd = fileno(file);
|
||||
if (ftruncate(fd, current_offset) != 0)
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetUniquePathNumber(const FilePath& path) {
|
||||
DCHECK(!path.empty());
|
||||
if (!PathExists(path))
|
||||
return 0;
|
||||
|
||||
std::string number;
|
||||
for (int count = 1; count <= kMaxUniqueFiles; ++count) {
|
||||
StringAppendF(&number, " (%d)", count);
|
||||
if (!PathExists(path.InsertBeforeExtensionASCII(number)))
|
||||
return count;
|
||||
number.clear();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
FilePath GetUniquePath(const FilePath& path) {
|
||||
DCHECK(!path.empty());
|
||||
const int uniquifier = GetUniquePathNumber(path);
|
||||
if (uniquifier > 0)
|
||||
return path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", uniquifier));
|
||||
return uniquifier == 0 ? path : base::FilePath();
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
bool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes) {
|
||||
DCHECK_GE(max_bytes, 0);
|
||||
|
||||
File file(file_path, File::FLAG_OPEN | File::FLAG_READ |
|
||||
File::FLAG_SEQUENTIAL_SCAN |
|
||||
File::FLAG_SHARE_DELETE);
|
||||
if (!file.IsValid())
|
||||
return false;
|
||||
|
||||
constexpr int kBufferSize = 1024 * 1024;
|
||||
// Ensures the buffer is deallocated at function exit.
|
||||
std::unique_ptr<char[]> buffer_deleter(new char[kBufferSize]);
|
||||
char* const buffer = buffer_deleter.get();
|
||||
|
||||
while (max_bytes > 0) {
|
||||
// The static_cast<int> is safe because kBufferSize is int, and both values
|
||||
// are non-negative. So, the minimum is guaranteed to fit in int.
|
||||
const int read_size =
|
||||
static_cast<int>(std::min<int64_t>(max_bytes, kBufferSize));
|
||||
DCHECK_GE(read_size, 0);
|
||||
DCHECK_LE(read_size, kBufferSize);
|
||||
|
||||
const int read_bytes = file.ReadAtCurrentPos(buffer, read_size);
|
||||
if (read_bytes < 0)
|
||||
return false;
|
||||
if (read_bytes == 0)
|
||||
break;
|
||||
|
||||
max_bytes -= read_bytes;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
#endif // !defined(OS_NACL_NONSFI)
|
||||
|
||||
} // namespace base
|
||||
575
TMessagesProj/jni/voip/webrtc/base/files/file_util.h
Normal file
575
TMessagesProj/jni/voip/webrtc/base/files/file_util.h
Normal file
|
|
@ -0,0 +1,575 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This file contains utility functions for dealing with the local
|
||||
// filesystem.
|
||||
|
||||
#ifndef BASE_FILES_FILE_UTIL_H_
|
||||
#define BASE_FILES_FILE_UTIL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/win/windows_types.h"
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
#include "base/file_descriptor_posix.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
class Environment;
|
||||
class Time;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions that involve filesystem access or modification:
|
||||
|
||||
// Returns an absolute version of a relative path. Returns an empty path on
|
||||
// error. On POSIX, this function fails if the path does not exist. This
|
||||
// function can result in I/O so it can be slow.
|
||||
BASE_EXPORT FilePath MakeAbsoluteFilePath(const FilePath& input);
|
||||
|
||||
// Returns the total number of bytes used by all the files under |root_path|.
|
||||
// If the path does not exist the function returns 0.
|
||||
//
|
||||
// This function is implemented using the FileEnumerator class so it is not
|
||||
// particularly speedy in any platform.
|
||||
BASE_EXPORT int64_t ComputeDirectorySize(const FilePath& root_path);
|
||||
|
||||
// Deletes the given path, whether it's a file or a directory.
|
||||
// If it's a directory, it's perfectly happy to delete all of the
|
||||
// directory's contents. Passing true to recursive deletes
|
||||
// subdirectories and their contents as well.
|
||||
// Returns true if successful, false otherwise. It is considered successful
|
||||
// to attempt to delete a file that does not exist.
|
||||
//
|
||||
// In POSIX environment and if |path| is a symbolic link, this deletes only
|
||||
// the symlink. (even if the symlink points to a non-existent file)
|
||||
//
|
||||
// WARNING: USING THIS WITH recursive==true IS EQUIVALENT
|
||||
// TO "rm -rf", SO USE WITH CAUTION.
|
||||
//
|
||||
// Note: The |recursive| parameter is in the process of being removed. Use
|
||||
// DeleteFileRecursively() instead. See https://crbug.com/1009837
|
||||
BASE_EXPORT bool DeleteFile(const FilePath& path, bool recursive);
|
||||
|
||||
// Deletes the given path, whether it's a file or a directory.
|
||||
// If it's a directory, it's perfectly happy to delete all of the
|
||||
// directory's contents, including subdirectories and their contents.
|
||||
// Returns true if successful, false otherwise. It is considered successful
|
||||
// to attempt to delete a file that does not exist.
|
||||
//
|
||||
// In POSIX environment and if |path| is a symbolic link, this deletes only
|
||||
// the symlink. (even if the symlink points to a non-existent file)
|
||||
//
|
||||
// WARNING: USING THIS EQUIVALENT TO "rm -rf", SO USE WITH CAUTION.
|
||||
BASE_EXPORT bool DeleteFileRecursively(const FilePath& path);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Schedules to delete the given path, whether it's a file or a directory, until
|
||||
// the operating system is restarted.
|
||||
// Note:
|
||||
// 1) The file/directory to be deleted should exist in a temp folder.
|
||||
// 2) The directory to be deleted must be empty.
|
||||
BASE_EXPORT bool DeleteFileAfterReboot(const FilePath& path);
|
||||
#endif
|
||||
|
||||
// Moves the given path, whether it's a file or a directory.
|
||||
// If a simple rename is not possible, such as in the case where the paths are
|
||||
// on different volumes, this will attempt to copy and delete. Returns
|
||||
// true for success.
|
||||
// This function fails if either path contains traversal components ('..').
|
||||
BASE_EXPORT bool Move(const FilePath& from_path, const FilePath& to_path);
|
||||
|
||||
// Renames file |from_path| to |to_path|. Both paths must be on the same
|
||||
// volume, or the function will fail. Destination file will be created
|
||||
// if it doesn't exist. Prefer this function over Move when dealing with
|
||||
// temporary files. On Windows it preserves attributes of the target file.
|
||||
// Returns true on success, leaving *error unchanged.
|
||||
// Returns false on failure and sets *error appropriately, if it is non-NULL.
|
||||
BASE_EXPORT bool ReplaceFile(const FilePath& from_path,
|
||||
const FilePath& to_path,
|
||||
File::Error* error);
|
||||
|
||||
// Copies a single file. Use CopyDirectory() to copy directories.
|
||||
// This function fails if either path contains traversal components ('..').
|
||||
// This function also fails if |to_path| is a directory.
|
||||
//
|
||||
// On POSIX, if |to_path| is a symlink, CopyFile() will follow the symlink. This
|
||||
// may have security implications. Use with care.
|
||||
//
|
||||
// If |to_path| already exists and is a regular file, it will be overwritten,
|
||||
// though its permissions will stay the same.
|
||||
//
|
||||
// If |to_path| does not exist, it will be created. The new file's permissions
|
||||
// varies per platform:
|
||||
//
|
||||
// - This function keeps the metadata on Windows. The read only bit is not kept.
|
||||
// - On Mac and iOS, |to_path| retains |from_path|'s permissions, except user
|
||||
// read/write permissions are always set.
|
||||
// - On Linux and Android, |to_path| has user read/write permissions only. i.e.
|
||||
// Always 0600.
|
||||
// - On ChromeOS, |to_path| has user read/write permissions and group/others
|
||||
// read permissions. i.e. Always 0644.
|
||||
BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path);
|
||||
|
||||
// Copies the given path, and optionally all subdirectories and their contents
|
||||
// as well.
|
||||
//
|
||||
// If there are files existing under to_path, always overwrite. Returns true
|
||||
// if successful, false otherwise. Wildcards on the names are not supported.
|
||||
//
|
||||
// This function has the same metadata behavior as CopyFile().
|
||||
//
|
||||
// If you only need to copy a file use CopyFile, it's faster.
|
||||
BASE_EXPORT bool CopyDirectory(const FilePath& from_path,
|
||||
const FilePath& to_path,
|
||||
bool recursive);
|
||||
|
||||
// Like CopyDirectory() except trying to overwrite an existing file will not
|
||||
// work and will return false.
|
||||
BASE_EXPORT bool CopyDirectoryExcl(const FilePath& from_path,
|
||||
const FilePath& to_path,
|
||||
bool recursive);
|
||||
|
||||
// Returns true if the given path exists on the local filesystem,
|
||||
// false otherwise.
|
||||
BASE_EXPORT bool PathExists(const FilePath& path);
|
||||
|
||||
// Returns true if the given path is writable by the user, false otherwise.
|
||||
BASE_EXPORT bool PathIsWritable(const FilePath& path);
|
||||
|
||||
// Returns true if the given path exists and is a directory, false otherwise.
|
||||
BASE_EXPORT bool DirectoryExists(const FilePath& path);
|
||||
|
||||
// Returns true if the contents of the two files given are equal, false
|
||||
// otherwise. If either file can't be read, returns false.
|
||||
BASE_EXPORT bool ContentsEqual(const FilePath& filename1,
|
||||
const FilePath& filename2);
|
||||
|
||||
// Returns true if the contents of the two text files given are equal, false
|
||||
// otherwise. This routine treats "\r\n" and "\n" as equivalent.
|
||||
BASE_EXPORT bool TextContentsEqual(const FilePath& filename1,
|
||||
const FilePath& filename2);
|
||||
|
||||
// Reads the file at |path| into |contents| and returns true on success and
|
||||
// false on error. For security reasons, a |path| containing path traversal
|
||||
// components ('..') is treated as a read error and |contents| is set to empty.
|
||||
// In case of I/O error, |contents| holds the data that could be read from the
|
||||
// file before the error occurred.
|
||||
// |contents| may be NULL, in which case this function is useful for its side
|
||||
// effect of priming the disk cache (could be used for unit tests).
|
||||
BASE_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents);
|
||||
|
||||
// Reads the file at |path| into |contents| and returns true on success and
|
||||
// false on error. For security reasons, a |path| containing path traversal
|
||||
// components ('..') is treated as a read error and |contents| is set to empty.
|
||||
// In case of I/O error, |contents| holds the data that could be read from the
|
||||
// file before the error occurred. When the file size exceeds |max_size|, the
|
||||
// function returns false with |contents| holding the file truncated to
|
||||
// |max_size|.
|
||||
// |contents| may be NULL, in which case this function is useful for its side
|
||||
// effect of priming the disk cache (could be used for unit tests).
|
||||
BASE_EXPORT bool ReadFileToStringWithMaxSize(const FilePath& path,
|
||||
std::string* contents,
|
||||
size_t max_size);
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
|
||||
// Read exactly |bytes| bytes from file descriptor |fd|, storing the result
|
||||
// in |buffer|. This function is protected against EINTR and partial reads.
|
||||
// Returns true iff |bytes| bytes have been successfully read from |fd|.
|
||||
BASE_EXPORT bool ReadFromFD(int fd, char* buffer, size_t bytes);
|
||||
|
||||
// Performs the same function as CreateAndOpenTemporaryFileInDir(), but returns
|
||||
// the file-descriptor wrapped in a ScopedFD, rather than wrapped in a FILE.
|
||||
BASE_EXPORT ScopedFD CreateAndOpenFdForTemporaryFileInDir(const FilePath& dir,
|
||||
FilePath* path);
|
||||
|
||||
#endif // defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
|
||||
// Creates a symbolic link at |symlink| pointing to |target|. Returns
|
||||
// false on failure.
|
||||
BASE_EXPORT bool CreateSymbolicLink(const FilePath& target,
|
||||
const FilePath& symlink);
|
||||
|
||||
// Reads the given |symlink| and returns where it points to in |target|.
|
||||
// Returns false upon failure.
|
||||
BASE_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target);
|
||||
|
||||
// Bits and masks of the file permission.
|
||||
enum FilePermissionBits {
|
||||
FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO,
|
||||
FILE_PERMISSION_USER_MASK = S_IRWXU,
|
||||
FILE_PERMISSION_GROUP_MASK = S_IRWXG,
|
||||
FILE_PERMISSION_OTHERS_MASK = S_IRWXO,
|
||||
|
||||
FILE_PERMISSION_READ_BY_USER = S_IRUSR,
|
||||
FILE_PERMISSION_WRITE_BY_USER = S_IWUSR,
|
||||
FILE_PERMISSION_EXECUTE_BY_USER = S_IXUSR,
|
||||
FILE_PERMISSION_READ_BY_GROUP = S_IRGRP,
|
||||
FILE_PERMISSION_WRITE_BY_GROUP = S_IWGRP,
|
||||
FILE_PERMISSION_EXECUTE_BY_GROUP = S_IXGRP,
|
||||
FILE_PERMISSION_READ_BY_OTHERS = S_IROTH,
|
||||
FILE_PERMISSION_WRITE_BY_OTHERS = S_IWOTH,
|
||||
FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH,
|
||||
};
|
||||
|
||||
// Reads the permission of the given |path|, storing the file permission
|
||||
// bits in |mode|. If |path| is symbolic link, |mode| is the permission of
|
||||
// a file which the symlink points to.
|
||||
BASE_EXPORT bool GetPosixFilePermissions(const FilePath& path, int* mode);
|
||||
// Sets the permission of the given |path|. If |path| is symbolic link, sets
|
||||
// the permission of a file which the symlink points to.
|
||||
BASE_EXPORT bool SetPosixFilePermissions(const FilePath& path, int mode);
|
||||
|
||||
// Returns true iff |executable| can be found in any directory specified by the
|
||||
// environment variable in |env|.
|
||||
BASE_EXPORT bool ExecutableExistsInPath(Environment* env,
|
||||
const FilePath::StringType& executable);
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_AIX)
|
||||
// Determine if files under a given |path| can be mapped and then mprotect'd
|
||||
// PROT_EXEC. This depends on the mount options used for |path|, which vary
|
||||
// among different Linux distributions and possibly local configuration. It also
|
||||
// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm
|
||||
// but its kernel allows mprotect with PROT_EXEC anyway.
|
||||
BASE_EXPORT bool IsPathExecutable(const FilePath& path);
|
||||
#endif // OS_LINUX || OS_AIX
|
||||
|
||||
#endif // OS_POSIX
|
||||
|
||||
// Returns true if the given directory is empty
|
||||
BASE_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path);
|
||||
|
||||
// Get the temporary directory provided by the system.
|
||||
//
|
||||
// WARNING: In general, you should use CreateTemporaryFile variants below
|
||||
// instead of this function. Those variants will ensure that the proper
|
||||
// permissions are set so that other users on the system can't edit them while
|
||||
// they're open (which can lead to security issues).
|
||||
BASE_EXPORT bool GetTempDir(FilePath* path);
|
||||
|
||||
// Get the home directory. This is more complicated than just getenv("HOME")
|
||||
// as it knows to fall back on getpwent() etc.
|
||||
//
|
||||
// You should not generally call this directly. Instead use DIR_HOME with the
|
||||
// path service which will use this function but cache the value.
|
||||
// Path service may also override DIR_HOME.
|
||||
BASE_EXPORT FilePath GetHomeDir();
|
||||
|
||||
// Creates a temporary file. The full path is placed in |path|, and the
|
||||
// function returns true if was successful in creating the file. The file will
|
||||
// be empty and all handles closed after this function returns.
|
||||
BASE_EXPORT bool CreateTemporaryFile(FilePath* path);
|
||||
|
||||
// Same as CreateTemporaryFile but the file is created in |dir|.
|
||||
BASE_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir,
|
||||
FilePath* temp_file);
|
||||
|
||||
// Create and open a temporary file. File is opened for read/write.
|
||||
// The full path is placed in |path|.
|
||||
// Returns a handle to the opened file or NULL if an error occurred.
|
||||
BASE_EXPORT FILE* CreateAndOpenTemporaryFile(FilePath* path);
|
||||
|
||||
// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|.
|
||||
BASE_EXPORT FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir,
|
||||
FilePath* path);
|
||||
|
||||
// Create a new directory. If prefix is provided, the new directory name is in
|
||||
// the format of prefixyyyy.
|
||||
// NOTE: prefix is ignored in the POSIX implementation.
|
||||
// If success, return true and output the full path of the directory created.
|
||||
BASE_EXPORT bool CreateNewTempDirectory(const FilePath::StringType& prefix,
|
||||
FilePath* new_temp_path);
|
||||
|
||||
// Create a directory within another directory.
|
||||
// Extra characters will be appended to |prefix| to ensure that the
|
||||
// new directory does not have the same name as an existing directory.
|
||||
BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir,
|
||||
const FilePath::StringType& prefix,
|
||||
FilePath* new_dir);
|
||||
|
||||
// Creates a directory, as well as creating any parent directories, if they
|
||||
// don't exist. Returns 'true' on successful creation, or if the directory
|
||||
// already exists. The directory is only readable by the current user.
|
||||
// Returns true on success, leaving *error unchanged.
|
||||
// Returns false on failure and sets *error appropriately, if it is non-NULL.
|
||||
BASE_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path,
|
||||
File::Error* error);
|
||||
|
||||
// Backward-compatible convenience method for the above.
|
||||
BASE_EXPORT bool CreateDirectory(const FilePath& full_path);
|
||||
|
||||
// Returns the file size. Returns true on success.
|
||||
BASE_EXPORT bool GetFileSize(const FilePath& file_path, int64_t* file_size);
|
||||
|
||||
// Sets |real_path| to |path| with symbolic links and junctions expanded.
|
||||
// On windows, make sure the path starts with a lettered drive.
|
||||
// |path| must reference a file. Function will fail if |path| points to
|
||||
// a directory or to a nonexistent path. On windows, this function will
|
||||
// fail if |real_path| would be longer than MAX_PATH characters.
|
||||
BASE_EXPORT bool NormalizeFilePath(const FilePath& path, FilePath* real_path);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."),
|
||||
// return in |drive_letter_path| the equivalent path that starts with
|
||||
// a drive letter ("C:\..."). Return false if no such path exists.
|
||||
BASE_EXPORT bool DevicePathToDriveLetterPath(const FilePath& device_path,
|
||||
FilePath* drive_letter_path);
|
||||
|
||||
// Method that wraps the win32 GetLongPathName API, normalizing the specified
|
||||
// path to its long form. An example where this is needed is when comparing
|
||||
// temp file paths. If a username isn't a valid 8.3 short file name (even just a
|
||||
// lengthy name like "user with long name"), Windows will set the TMP and TEMP
|
||||
// environment variables to be 8.3 paths. ::GetTempPath (called in
|
||||
// base::GetTempDir) just uses the value specified by TMP or TEMP, and so can
|
||||
// return a short path. Returns an empty path on error.
|
||||
BASE_EXPORT FilePath MakeLongFilePath(const FilePath& input);
|
||||
|
||||
// Creates a hard link named |to_file| to the file |from_file|. Both paths
|
||||
// must be on the same volume, and |from_file| may not name a directory.
|
||||
// Returns true if the hard link is created, false if it fails.
|
||||
BASE_EXPORT bool CreateWinHardLink(const FilePath& to_file,
|
||||
const FilePath& from_file);
|
||||
#endif
|
||||
|
||||
// This function will return if the given file is a symlink or not.
|
||||
BASE_EXPORT bool IsLink(const FilePath& file_path);
|
||||
|
||||
// Returns information about the given file path.
|
||||
BASE_EXPORT bool GetFileInfo(const FilePath& file_path, File::Info* info);
|
||||
|
||||
// Sets the time of the last access and the time of the last modification.
|
||||
BASE_EXPORT bool TouchFile(const FilePath& path,
|
||||
const Time& last_accessed,
|
||||
const Time& last_modified);
|
||||
|
||||
// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. The
|
||||
// underlying file descriptor (POSIX) or handle (Windows) is unconditionally
|
||||
// configured to not be propagated to child processes.
|
||||
BASE_EXPORT FILE* OpenFile(const FilePath& filename, const char* mode);
|
||||
|
||||
// Closes file opened by OpenFile. Returns true on success.
|
||||
BASE_EXPORT bool CloseFile(FILE* file);
|
||||
|
||||
// Associates a standard FILE stream with an existing File. Note that this
|
||||
// functions take ownership of the existing File.
|
||||
BASE_EXPORT FILE* FileToFILE(File file, const char* mode);
|
||||
|
||||
// Truncates an open file to end at the location of the current file pointer.
|
||||
// This is a cross-platform analog to Windows' SetEndOfFile() function.
|
||||
BASE_EXPORT bool TruncateFile(FILE* file);
|
||||
|
||||
// Reads at most the given number of bytes from the file into the buffer.
|
||||
// Returns the number of read bytes, or -1 on error.
|
||||
BASE_EXPORT int ReadFile(const FilePath& filename, char* data, int max_size);
|
||||
|
||||
// Writes the given buffer into the file, overwriting any data that was
|
||||
// previously there. Returns the number of bytes written, or -1 on error.
|
||||
// If file doesn't exist, it gets created with read/write permissions for all.
|
||||
BASE_EXPORT int WriteFile(const FilePath& filename, const char* data,
|
||||
int size);
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// Appends |data| to |fd|. Does not close |fd| when done. Returns true iff
|
||||
// |size| bytes of |data| were written to |fd|.
|
||||
BASE_EXPORT bool WriteFileDescriptor(const int fd, const char* data, int size);
|
||||
|
||||
// Allocates disk space for the file referred to by |fd| for the byte range
|
||||
// starting at |offset| and continuing for |size| bytes. The file size will be
|
||||
// changed if |offset|+|len| is greater than the file size. Zeros will fill the
|
||||
// new space.
|
||||
// After a successful call, subsequent writes into the specified range are
|
||||
// guaranteed not to fail because of lack of disk space.
|
||||
BASE_EXPORT bool AllocateFileRegion(File* file, int64_t offset, size_t size);
|
||||
#endif
|
||||
|
||||
// Appends |data| to |filename|. Returns true iff |size| bytes of |data| were
|
||||
// written to |filename|.
|
||||
BASE_EXPORT bool AppendToFile(const FilePath& filename,
|
||||
const char* data,
|
||||
int size);
|
||||
|
||||
// Gets the current working directory for the process.
|
||||
BASE_EXPORT bool GetCurrentDirectory(FilePath* path);
|
||||
|
||||
// Sets the current working directory for the process.
|
||||
BASE_EXPORT bool SetCurrentDirectory(const FilePath& path);
|
||||
|
||||
// The largest value attempted by GetUniquePath{Number,}.
|
||||
enum { kMaxUniqueFiles = 100 };
|
||||
|
||||
// Returns the number N that makes |path| unique when formatted as " (N)" in a
|
||||
// suffix to its basename before any file extension, where N is a number between
|
||||
// 1 and 100 (inclusive). Returns 0 if |path| does not exist (meaning that it is
|
||||
// unique as-is), or -1 if no such number can be found.
|
||||
BASE_EXPORT int GetUniquePathNumber(const FilePath& path);
|
||||
|
||||
// Returns |path| if it does not exist. Otherwise, returns |path| with the
|
||||
// suffix " (N)" appended to its basename before any file extension, where N is
|
||||
// a number between 1 and 100 (inclusive). Returns an empty path if no such
|
||||
// number can be found.
|
||||
BASE_EXPORT FilePath GetUniquePath(const FilePath& path);
|
||||
|
||||
// Sets the given |fd| to non-blocking mode.
|
||||
// Returns true if it was able to set it in the non-blocking mode, otherwise
|
||||
// false.
|
||||
BASE_EXPORT bool SetNonBlocking(int fd);
|
||||
|
||||
// Hints the OS to prefetch the first |max_bytes| of |file_path| into its cache.
|
||||
//
|
||||
// If called at the appropriate time, this can reduce the latency incurred by
|
||||
// feature code that needs to read the file.
|
||||
//
|
||||
// |max_bytes| specifies how many bytes should be pre-fetched. It may exceed the
|
||||
// file's size. Passing in std::numeric_limits<int64_t>::max() is a convenient
|
||||
// way to get the entire file pre-fetched.
|
||||
//
|
||||
// |is_executable| specifies whether the file is to be prefetched as
|
||||
// executable code or as data. Windows treats the file backed pages in RAM
|
||||
// differently, and specifying the wrong value results in two copies in RAM.
|
||||
//
|
||||
// Returns false if prefetching definitely failed. A return value of true does
|
||||
// not guarantee that the entire desired range was prefetched.
|
||||
//
|
||||
// Calling this before using ::LoadLibrary() on Windows is more efficient memory
|
||||
// wise, but we must be sure no other threads try to LoadLibrary() the file
|
||||
// while we are doing the mapping and prefetching, or the process will get a
|
||||
// private copy of the DLL via COW.
|
||||
BASE_EXPORT bool PreReadFile(
|
||||
const FilePath& file_path,
|
||||
bool is_executable,
|
||||
int64_t max_bytes = std::numeric_limits<int64_t>::max());
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
|
||||
// Creates a pipe. Returns true on success, otherwise false.
|
||||
// On success, |read_fd| will be set to the fd of the read side, and
|
||||
// |write_fd| will be set to the one of write side. If |non_blocking|
|
||||
// is set the pipe will be created with O_NONBLOCK|O_CLOEXEC flags set
|
||||
// otherwise flag is set to zero (default).
|
||||
BASE_EXPORT bool CreatePipe(ScopedFD* read_fd,
|
||||
ScopedFD* write_fd,
|
||||
bool non_blocking = false);
|
||||
|
||||
// Creates a non-blocking, close-on-exec pipe.
|
||||
// This creates a non-blocking pipe that is not intended to be shared with any
|
||||
// child process. This will be done atomically if the operating system supports
|
||||
// it. Returns true if it was able to create the pipe, otherwise false.
|
||||
BASE_EXPORT bool CreateLocalNonBlockingPipe(int fds[2]);
|
||||
|
||||
// Sets the given |fd| to close-on-exec mode.
|
||||
// Returns true if it was able to set it in the close-on-exec mode, otherwise
|
||||
// false.
|
||||
BASE_EXPORT bool SetCloseOnExec(int fd);
|
||||
|
||||
// Test that |path| can only be changed by a given user and members of
|
||||
// a given set of groups.
|
||||
// Specifically, test that all parts of |path| under (and including) |base|:
|
||||
// * Exist.
|
||||
// * Are owned by a specific user.
|
||||
// * Are not writable by all users.
|
||||
// * Are owned by a member of a given set of groups, or are not writable by
|
||||
// their group.
|
||||
// * Are not symbolic links.
|
||||
// This is useful for checking that a config file is administrator-controlled.
|
||||
// |base| must contain |path|.
|
||||
BASE_EXPORT bool VerifyPathControlledByUser(const base::FilePath& base,
|
||||
const base::FilePath& path,
|
||||
uid_t owner_uid,
|
||||
const std::set<gid_t>& group_gids);
|
||||
#endif // defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
|
||||
#if defined(OS_MACOSX) && !defined(OS_IOS)
|
||||
// Is |path| writable only by a user with administrator privileges?
|
||||
// This function uses Mac OS conventions. The super user is assumed to have
|
||||
// uid 0, and the administrator group is assumed to be named "admin".
|
||||
// Testing that |path|, and every parent directory including the root of
|
||||
// the filesystem, are owned by the superuser, controlled by the group
|
||||
// "admin", are not writable by all users, and contain no symbolic links.
|
||||
// Will return false if |path| does not exist.
|
||||
BASE_EXPORT bool VerifyPathControlledByAdmin(const base::FilePath& path);
|
||||
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
|
||||
|
||||
// Returns the maximum length of path component on the volume containing
|
||||
// the directory |path|, in the number of FilePath::CharType, or -1 on failure.
|
||||
BASE_EXPORT int GetMaximumPathComponentLength(const base::FilePath& path);
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_AIX)
|
||||
// Broad categories of file systems as returned by statfs() on Linux.
|
||||
enum FileSystemType {
|
||||
FILE_SYSTEM_UNKNOWN, // statfs failed.
|
||||
FILE_SYSTEM_0, // statfs.f_type == 0 means unknown, may indicate AFS.
|
||||
FILE_SYSTEM_ORDINARY, // on-disk filesystem like ext2
|
||||
FILE_SYSTEM_NFS,
|
||||
FILE_SYSTEM_SMB,
|
||||
FILE_SYSTEM_CODA,
|
||||
FILE_SYSTEM_MEMORY, // in-memory file system
|
||||
FILE_SYSTEM_CGROUP, // cgroup control.
|
||||
FILE_SYSTEM_OTHER, // any other value.
|
||||
FILE_SYSTEM_TYPE_COUNT
|
||||
};
|
||||
|
||||
// Attempts determine the FileSystemType for |path|.
|
||||
// Returns false if |path| doesn't exist.
|
||||
BASE_EXPORT bool GetFileSystemType(const FilePath& path, FileSystemType* type);
|
||||
#endif
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// Get a temporary directory for shared memory files. The directory may depend
|
||||
// on whether the destination is intended for executable files, which in turn
|
||||
// depends on how /dev/shmem was mounted. As a result, you must supply whether
|
||||
// you intend to create executable shmem segments so this function can find
|
||||
// an appropriate location.
|
||||
BASE_EXPORT bool GetShmemTempDir(bool executable, FilePath* path);
|
||||
#endif
|
||||
|
||||
// Internal --------------------------------------------------------------------
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Same as Move but allows paths with traversal components.
|
||||
// Use only with extreme care.
|
||||
BASE_EXPORT bool MoveUnsafe(const FilePath& from_path,
|
||||
const FilePath& to_path);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Copy from_path to to_path recursively and then delete from_path recursively.
|
||||
// Returns true if all operations succeed.
|
||||
// This function simulates Move(), but unlike Move() it works across volumes.
|
||||
// This function is not transactional.
|
||||
BASE_EXPORT bool CopyAndDeleteDirectory(const FilePath& from_path,
|
||||
const FilePath& to_path);
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
// Used by PreReadFile() when no kernel support for prefetching is available.
|
||||
bool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_FILE_UTIL_H_
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/path_service.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
bool GetShmemTempDir(bool executable, base::FilePath* path) {
|
||||
return PathService::Get(base::DIR_CACHE, path);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
63
TMessagesProj/jni/voip/webrtc/base/files/file_util_linux.cc
Normal file
63
TMessagesProj/jni/voip/webrtc/base/files/file_util_linux.cc
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
bool GetFileSystemType(const FilePath& path, FileSystemType* type) {
|
||||
struct statfs statfs_buf;
|
||||
if (statfs(path.value().c_str(), &statfs_buf) < 0) {
|
||||
if (errno == ENOENT)
|
||||
return false;
|
||||
*type = FILE_SYSTEM_UNKNOWN;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not all possible |statfs_buf.f_type| values are in linux/magic.h.
|
||||
// Missing values are copied from the statfs man page.
|
||||
switch (statfs_buf.f_type) {
|
||||
case 0:
|
||||
*type = FILE_SYSTEM_0;
|
||||
break;
|
||||
case EXT2_SUPER_MAGIC: // Also ext3 and ext4
|
||||
case MSDOS_SUPER_MAGIC:
|
||||
case REISERFS_SUPER_MAGIC:
|
||||
case BTRFS_SUPER_MAGIC:
|
||||
case 0x5346544E: // NTFS
|
||||
case 0x58465342: // XFS
|
||||
case 0x3153464A: // JFS
|
||||
*type = FILE_SYSTEM_ORDINARY;
|
||||
break;
|
||||
case NFS_SUPER_MAGIC:
|
||||
*type = FILE_SYSTEM_NFS;
|
||||
break;
|
||||
case SMB_SUPER_MAGIC:
|
||||
case 0xFF534D42: // CIFS
|
||||
*type = FILE_SYSTEM_SMB;
|
||||
break;
|
||||
case CODA_SUPER_MAGIC:
|
||||
*type = FILE_SYSTEM_CODA;
|
||||
break;
|
||||
case HUGETLBFS_MAGIC:
|
||||
case RAMFS_MAGIC:
|
||||
case TMPFS_MAGIC:
|
||||
*type = FILE_SYSTEM_MEMORY;
|
||||
break;
|
||||
case CGROUP_SUPER_MAGIC:
|
||||
*type = FILE_SYSTEM_CGROUP;
|
||||
break;
|
||||
default:
|
||||
*type = FILE_SYSTEM_OTHER;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
66
TMessagesProj/jni/voip/webrtc/base/files/file_util_mac.mm
Normal file
66
TMessagesProj/jni/voip/webrtc/base/files/file_util_mac.mm
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <copyfile.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/foundation_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
if (from_path.ReferencesParent() || to_path.ReferencesParent())
|
||||
return false;
|
||||
return (copyfile(from_path.value().c_str(),
|
||||
to_path.value().c_str(), NULL, COPYFILE_DATA) == 0);
|
||||
}
|
||||
|
||||
bool GetTempDir(base::FilePath* path) {
|
||||
// In order to facilitate hermetic runs on macOS, first check
|
||||
// $MAC_CHROMIUM_TMPDIR. We check this instead of $TMPDIR because external
|
||||
// programs currently set $TMPDIR with no effect, but when we respect it
|
||||
// directly it can cause crashes (like crbug.com/698759).
|
||||
const char* env_tmpdir = getenv("MAC_CHROMIUM_TMPDIR");
|
||||
if (env_tmpdir) {
|
||||
DCHECK_LT(strlen(env_tmpdir), 50u)
|
||||
<< "too-long TMPDIR causes socket name length issues.";
|
||||
*path = base::FilePath(env_tmpdir);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we didn't find it, fall back to the native function.
|
||||
NSString* tmp = NSTemporaryDirectory();
|
||||
if (tmp == nil)
|
||||
return false;
|
||||
*path = base::mac::NSStringToFilePath(tmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
FilePath GetHomeDir() {
|
||||
NSString* tmp = NSHomeDirectory();
|
||||
if (tmp != nil) {
|
||||
FilePath mac_home_dir = base::mac::NSStringToFilePath(tmp);
|
||||
if (!mac_home_dir.empty())
|
||||
return mac_home_dir;
|
||||
}
|
||||
|
||||
// Fall back on temp dir if no home directory is defined.
|
||||
FilePath rv;
|
||||
if (GetTempDir(&rv))
|
||||
return rv;
|
||||
|
||||
// Last resort.
|
||||
return FilePath("/tmp");
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
1191
TMessagesProj/jni/voip/webrtc/base/files/file_util_posix.cc
Normal file
1191
TMessagesProj/jni/voip/webrtc/base/files/file_util_posix.cc
Normal file
File diff suppressed because it is too large
Load diff
1060
TMessagesProj/jni/voip/webrtc/base/files/file_util_win.cc
Normal file
1060
TMessagesProj/jni/voip/webrtc/base/files/file_util_win.cc
Normal file
File diff suppressed because it is too large
Load diff
454
TMessagesProj/jni/voip/webrtc/base/files/file_win.cc
Normal file
454
TMessagesProj/jni/voip/webrtc/base/files/file_win.cc
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/file.h"
|
||||
|
||||
#include <io.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace base {
|
||||
|
||||
// Make sure our Whence mappings match the system headers.
|
||||
static_assert(File::FROM_BEGIN == FILE_BEGIN &&
|
||||
File::FROM_CURRENT == FILE_CURRENT &&
|
||||
File::FROM_END == FILE_END,
|
||||
"whence mapping must match the system headers");
|
||||
|
||||
bool File::IsValid() const {
|
||||
return file_.IsValid();
|
||||
}
|
||||
|
||||
PlatformFile File::GetPlatformFile() const {
|
||||
return file_.Get();
|
||||
}
|
||||
|
||||
PlatformFile File::TakePlatformFile() {
|
||||
return file_.Take();
|
||||
}
|
||||
|
||||
void File::Close() {
|
||||
if (!file_.IsValid())
|
||||
return;
|
||||
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
SCOPED_FILE_TRACE("Close");
|
||||
file_.Close();
|
||||
}
|
||||
|
||||
int64_t File::Seek(Whence whence, int64_t offset) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset);
|
||||
|
||||
LARGE_INTEGER distance, res;
|
||||
distance.QuadPart = offset;
|
||||
DWORD move_method = static_cast<DWORD>(whence);
|
||||
if (!SetFilePointerEx(file_.Get(), distance, &res, move_method))
|
||||
return -1;
|
||||
return res.QuadPart;
|
||||
}
|
||||
|
||||
int File::Read(int64_t offset, char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
DCHECK(!async_);
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("Read", size);
|
||||
|
||||
LARGE_INTEGER offset_li;
|
||||
offset_li.QuadPart = offset;
|
||||
|
||||
OVERLAPPED overlapped = {};
|
||||
overlapped.Offset = offset_li.LowPart;
|
||||
overlapped.OffsetHigh = offset_li.HighPart;
|
||||
|
||||
DWORD bytes_read;
|
||||
if (::ReadFile(file_.Get(), data, size, &bytes_read, &overlapped))
|
||||
return bytes_read;
|
||||
if (ERROR_HANDLE_EOF == GetLastError())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int File::ReadAtCurrentPos(char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
DCHECK(!async_);
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size);
|
||||
|
||||
DWORD bytes_read;
|
||||
if (::ReadFile(file_.Get(), data, size, &bytes_read, NULL))
|
||||
return bytes_read;
|
||||
if (ERROR_HANDLE_EOF == GetLastError())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
|
||||
// TODO(dbeam): trace this separately?
|
||||
return Read(offset, data, size);
|
||||
}
|
||||
|
||||
int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
|
||||
// TODO(dbeam): trace this separately?
|
||||
return ReadAtCurrentPos(data, size);
|
||||
}
|
||||
|
||||
int File::Write(int64_t offset, const char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
DCHECK(!async_);
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("Write", size);
|
||||
|
||||
LARGE_INTEGER offset_li;
|
||||
offset_li.QuadPart = offset;
|
||||
|
||||
OVERLAPPED overlapped = {};
|
||||
overlapped.Offset = offset_li.LowPart;
|
||||
overlapped.OffsetHigh = offset_li.HighPart;
|
||||
|
||||
DWORD bytes_written;
|
||||
if (::WriteFile(file_.Get(), data, size, &bytes_written, &overlapped))
|
||||
return bytes_written;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int File::WriteAtCurrentPos(const char* data, int size) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
DCHECK(!async_);
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size);
|
||||
|
||||
DWORD bytes_written;
|
||||
if (::WriteFile(file_.Get(), data, size, &bytes_written, NULL))
|
||||
return bytes_written;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
|
||||
return WriteAtCurrentPos(data, size);
|
||||
}
|
||||
|
||||
int64_t File::GetLength() {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE("GetLength");
|
||||
|
||||
LARGE_INTEGER size;
|
||||
if (!::GetFileSizeEx(file_.Get(), &size))
|
||||
return -1;
|
||||
|
||||
return static_cast<int64_t>(size.QuadPart);
|
||||
}
|
||||
|
||||
bool File::SetLength(int64_t length) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length);
|
||||
|
||||
// Get the current file pointer.
|
||||
LARGE_INTEGER file_pointer;
|
||||
LARGE_INTEGER zero;
|
||||
zero.QuadPart = 0;
|
||||
if (!::SetFilePointerEx(file_.Get(), zero, &file_pointer, FILE_CURRENT))
|
||||
return false;
|
||||
|
||||
LARGE_INTEGER length_li;
|
||||
length_li.QuadPart = length;
|
||||
// If length > file size, SetFilePointerEx() should extend the file
|
||||
// with zeroes on all Windows standard file systems (NTFS, FATxx).
|
||||
if (!::SetFilePointerEx(file_.Get(), length_li, NULL, FILE_BEGIN))
|
||||
return false;
|
||||
|
||||
// Set the new file length and move the file pointer to its old position.
|
||||
// This is consistent with ftruncate()'s behavior, even when the file
|
||||
// pointer points to a location beyond the end of the file.
|
||||
// TODO(rvargas): Emulating ftruncate details seem suspicious and it is not
|
||||
// promised by the interface (nor was promised by PlatformFile). See if this
|
||||
// implementation detail can be removed.
|
||||
return ((::SetEndOfFile(file_.Get()) != FALSE) &&
|
||||
(::SetFilePointerEx(file_.Get(), file_pointer, NULL, FILE_BEGIN) !=
|
||||
FALSE));
|
||||
}
|
||||
|
||||
bool File::SetTimes(Time last_access_time, Time last_modified_time) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE("SetTimes");
|
||||
|
||||
FILETIME last_access_filetime = last_access_time.ToFileTime();
|
||||
FILETIME last_modified_filetime = last_modified_time.ToFileTime();
|
||||
return (::SetFileTime(file_.Get(), NULL, &last_access_filetime,
|
||||
&last_modified_filetime) != FALSE);
|
||||
}
|
||||
|
||||
bool File::GetInfo(Info* info) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE("GetInfo");
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION file_info;
|
||||
if (!GetFileInformationByHandle(file_.Get(), &file_info))
|
||||
return false;
|
||||
|
||||
LARGE_INTEGER size;
|
||||
size.HighPart = file_info.nFileSizeHigh;
|
||||
size.LowPart = file_info.nFileSizeLow;
|
||||
info->size = size.QuadPart;
|
||||
info->is_directory =
|
||||
(file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
info->is_symbolic_link = false; // Windows doesn't have symbolic links.
|
||||
info->last_modified = Time::FromFileTime(file_info.ftLastWriteTime);
|
||||
info->last_accessed = Time::FromFileTime(file_info.ftLastAccessTime);
|
||||
info->creation_time = Time::FromFileTime(file_info.ftCreationTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
DWORD LockFileFlagsForMode(File::LockMode mode) {
|
||||
DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
|
||||
switch (mode) {
|
||||
case File::LockMode::kShared:
|
||||
return flags;
|
||||
case File::LockMode::kExclusive:
|
||||
return flags | LOCKFILE_EXCLUSIVE_LOCK;
|
||||
}
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
File::Error File::Lock(File::LockMode mode) {
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE("Lock");
|
||||
|
||||
OVERLAPPED overlapped = {};
|
||||
BOOL result =
|
||||
LockFileEx(file_.Get(), LockFileFlagsForMode(mode), /*dwReserved=*/0,
|
||||
/*nNumberOfBytesToLockLow=*/MAXDWORD,
|
||||
/*nNumberOfBytesToLockHigh=*/MAXDWORD, &overlapped);
|
||||
if (!result)
|
||||
return GetLastFileError();
|
||||
return FILE_OK;
|
||||
}
|
||||
|
||||
File::Error File::Unlock() {
|
||||
DCHECK(IsValid());
|
||||
|
||||
SCOPED_FILE_TRACE("Unlock");
|
||||
|
||||
OVERLAPPED overlapped = {};
|
||||
BOOL result =
|
||||
UnlockFileEx(file_.Get(), /*dwReserved=*/0,
|
||||
/*nNumberOfBytesToLockLow=*/MAXDWORD,
|
||||
/*nNumberOfBytesToLockHigh=*/MAXDWORD, &overlapped);
|
||||
if (!result)
|
||||
return GetLastFileError();
|
||||
return FILE_OK;
|
||||
}
|
||||
|
||||
File File::Duplicate() const {
|
||||
if (!IsValid())
|
||||
return File();
|
||||
|
||||
SCOPED_FILE_TRACE("Duplicate");
|
||||
|
||||
HANDLE other_handle = nullptr;
|
||||
|
||||
if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle
|
||||
GetPlatformFile(),
|
||||
GetCurrentProcess(), // hTargetProcessHandle
|
||||
&other_handle,
|
||||
0, // dwDesiredAccess ignored due to SAME_ACCESS
|
||||
FALSE, // !bInheritHandle
|
||||
DUPLICATE_SAME_ACCESS)) {
|
||||
return File(GetLastFileError());
|
||||
}
|
||||
|
||||
return File(ScopedPlatformFile(other_handle), async());
|
||||
}
|
||||
|
||||
bool File::DeleteOnClose(bool delete_on_close) {
|
||||
FILE_DISPOSITION_INFO disposition = {delete_on_close};
|
||||
return ::SetFileInformationByHandle(GetPlatformFile(), FileDispositionInfo,
|
||||
&disposition, sizeof(disposition)) != 0;
|
||||
}
|
||||
|
||||
// Static.
|
||||
File::Error File::OSErrorToFileError(DWORD last_error) {
|
||||
switch (last_error) {
|
||||
case ERROR_SHARING_VIOLATION:
|
||||
return FILE_ERROR_IN_USE;
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
case ERROR_FILE_EXISTS:
|
||||
return FILE_ERROR_EXISTS;
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
return FILE_ERROR_NOT_FOUND;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
return FILE_ERROR_ACCESS_DENIED;
|
||||
case ERROR_TOO_MANY_OPEN_FILES:
|
||||
return FILE_ERROR_TOO_MANY_OPENED;
|
||||
case ERROR_OUTOFMEMORY:
|
||||
case ERROR_NOT_ENOUGH_MEMORY:
|
||||
return FILE_ERROR_NO_MEMORY;
|
||||
case ERROR_HANDLE_DISK_FULL:
|
||||
case ERROR_DISK_FULL:
|
||||
case ERROR_DISK_RESOURCES_EXHAUSTED:
|
||||
return FILE_ERROR_NO_SPACE;
|
||||
case ERROR_USER_MAPPED_FILE:
|
||||
return FILE_ERROR_INVALID_OPERATION;
|
||||
case ERROR_NOT_READY:
|
||||
case ERROR_SECTOR_NOT_FOUND:
|
||||
case ERROR_DEV_NOT_EXIST:
|
||||
case ERROR_IO_DEVICE:
|
||||
case ERROR_FILE_CORRUPT:
|
||||
case ERROR_DISK_CORRUPT:
|
||||
return FILE_ERROR_IO;
|
||||
default:
|
||||
UmaHistogramSparse("PlatformFile.UnknownErrors.Windows", last_error);
|
||||
// This function should only be called for errors.
|
||||
DCHECK_NE(static_cast<DWORD>(ERROR_SUCCESS), last_error);
|
||||
return FILE_ERROR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
void File::DoInitialize(const FilePath& path, uint32_t flags) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(!IsValid());
|
||||
|
||||
DWORD disposition = 0;
|
||||
|
||||
if (flags & FLAG_OPEN)
|
||||
disposition = OPEN_EXISTING;
|
||||
|
||||
if (flags & FLAG_CREATE) {
|
||||
DCHECK(!disposition);
|
||||
disposition = CREATE_NEW;
|
||||
}
|
||||
|
||||
if (flags & FLAG_OPEN_ALWAYS) {
|
||||
DCHECK(!disposition);
|
||||
disposition = OPEN_ALWAYS;
|
||||
}
|
||||
|
||||
if (flags & FLAG_CREATE_ALWAYS) {
|
||||
DCHECK(!disposition);
|
||||
DCHECK(flags & FLAG_WRITE);
|
||||
disposition = CREATE_ALWAYS;
|
||||
}
|
||||
|
||||
if (flags & FLAG_OPEN_TRUNCATED) {
|
||||
DCHECK(!disposition);
|
||||
DCHECK(flags & FLAG_WRITE);
|
||||
disposition = TRUNCATE_EXISTING;
|
||||
}
|
||||
|
||||
if (!disposition) {
|
||||
::SetLastError(ERROR_INVALID_PARAMETER);
|
||||
error_details_ = FILE_ERROR_FAILED;
|
||||
NOTREACHED();
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD access = 0;
|
||||
if (flags & FLAG_WRITE)
|
||||
access = GENERIC_WRITE;
|
||||
if (flags & FLAG_APPEND) {
|
||||
DCHECK(!access);
|
||||
access = FILE_APPEND_DATA;
|
||||
}
|
||||
if (flags & FLAG_READ)
|
||||
access |= GENERIC_READ;
|
||||
if (flags & FLAG_WRITE_ATTRIBUTES)
|
||||
access |= FILE_WRITE_ATTRIBUTES;
|
||||
if (flags & FLAG_EXECUTE)
|
||||
access |= GENERIC_EXECUTE;
|
||||
if (flags & FLAG_CAN_DELETE_ON_CLOSE)
|
||||
access |= DELETE;
|
||||
|
||||
DWORD sharing = (flags & FLAG_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ;
|
||||
if (!(flags & FLAG_EXCLUSIVE_WRITE))
|
||||
sharing |= FILE_SHARE_WRITE;
|
||||
if (flags & FLAG_SHARE_DELETE)
|
||||
sharing |= FILE_SHARE_DELETE;
|
||||
|
||||
DWORD create_flags = 0;
|
||||
if (flags & FLAG_ASYNC)
|
||||
create_flags |= FILE_FLAG_OVERLAPPED;
|
||||
if (flags & FLAG_TEMPORARY)
|
||||
create_flags |= FILE_ATTRIBUTE_TEMPORARY;
|
||||
if (flags & FLAG_HIDDEN)
|
||||
create_flags |= FILE_ATTRIBUTE_HIDDEN;
|
||||
if (flags & FLAG_DELETE_ON_CLOSE)
|
||||
create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
|
||||
if (flags & FLAG_BACKUP_SEMANTICS)
|
||||
create_flags |= FILE_FLAG_BACKUP_SEMANTICS;
|
||||
if (flags & FLAG_SEQUENTIAL_SCAN)
|
||||
create_flags |= FILE_FLAG_SEQUENTIAL_SCAN;
|
||||
|
||||
file_.Set(CreateFile(path.value().c_str(), access, sharing, NULL, disposition,
|
||||
create_flags, NULL));
|
||||
|
||||
if (file_.IsValid()) {
|
||||
error_details_ = FILE_OK;
|
||||
async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
|
||||
|
||||
if (flags & (FLAG_OPEN_ALWAYS))
|
||||
created_ = (ERROR_ALREADY_EXISTS != GetLastError());
|
||||
else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
|
||||
created_ = true;
|
||||
} else {
|
||||
error_details_ = GetLastFileError();
|
||||
}
|
||||
}
|
||||
|
||||
bool File::Flush() {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
DCHECK(IsValid());
|
||||
SCOPED_FILE_TRACE("Flush");
|
||||
|
||||
// On Windows 8 and above, FlushFileBuffers is guaranteed to flush the storage
|
||||
// device's internal buffers (if they exist) before returning.
|
||||
// https://blogs.msdn.microsoft.com/oldnewthing/20170510-00/?p=95505
|
||||
return ::FlushFileBuffers(file_.Get()) != FALSE;
|
||||
}
|
||||
|
||||
void File::SetPlatformFile(PlatformFile file) {
|
||||
file_.Set(file);
|
||||
}
|
||||
|
||||
// static
|
||||
File::Error File::GetLastFileError() {
|
||||
return File::OSErrorToFileError(GetLastError());
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/important_file_writer.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/callback_helpers.h"
|
||||
#include "base/critical_closure.h"
|
||||
#include "base/debug/alias.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/task_runner.h"
|
||||
#include "base/task_runner_util.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kDefaultCommitInterval = TimeDelta::FromSeconds(10);
|
||||
|
||||
// This enum is used to define the buckets for an enumerated UMA histogram.
|
||||
// Hence,
|
||||
// (a) existing enumerated constants should never be deleted or reordered, and
|
||||
// (b) new constants should only be appended at the end of the enumeration.
|
||||
enum TempFileFailure {
|
||||
FAILED_CREATING,
|
||||
FAILED_OPENING,
|
||||
FAILED_CLOSING, // Unused.
|
||||
FAILED_WRITING,
|
||||
FAILED_RENAMING,
|
||||
FAILED_FLUSHING,
|
||||
TEMP_FILE_FAILURE_MAX
|
||||
};
|
||||
|
||||
// Helper function to write samples to a histogram with a dynamically assigned
|
||||
// histogram name. Works with different error code types convertible to int
|
||||
// which is the actual argument type of UmaHistogramExactLinear.
|
||||
template <typename SampleType>
|
||||
void UmaHistogramExactLinearWithSuffix(const char* histogram_name,
|
||||
StringPiece histogram_suffix,
|
||||
SampleType add_sample,
|
||||
SampleType max_sample) {
|
||||
static_assert(std::is_convertible<SampleType, int>::value,
|
||||
"SampleType should be convertible to int");
|
||||
DCHECK(histogram_name);
|
||||
std::string histogram_full_name(histogram_name);
|
||||
if (!histogram_suffix.empty()) {
|
||||
histogram_full_name.append(".");
|
||||
histogram_full_name.append(histogram_suffix.data(),
|
||||
histogram_suffix.length());
|
||||
}
|
||||
UmaHistogramExactLinear(histogram_full_name, static_cast<int>(add_sample),
|
||||
static_cast<int>(max_sample));
|
||||
}
|
||||
|
||||
void LogFailure(const FilePath& path,
|
||||
StringPiece histogram_suffix,
|
||||
TempFileFailure failure_code,
|
||||
StringPiece message) {
|
||||
UmaHistogramExactLinearWithSuffix("ImportantFile.TempFileFailures",
|
||||
histogram_suffix, failure_code,
|
||||
TEMP_FILE_FAILURE_MAX);
|
||||
DPLOG(WARNING) << "temp file failure: " << path.value() << " : " << message;
|
||||
}
|
||||
|
||||
// Helper function to call WriteFileAtomically() with a
|
||||
// std::unique_ptr<std::string>.
|
||||
void WriteScopedStringToFileAtomically(
|
||||
const FilePath& path,
|
||||
std::unique_ptr<std::string> data,
|
||||
OnceClosure before_write_callback,
|
||||
OnceCallback<void(bool success)> after_write_callback,
|
||||
const std::string& histogram_suffix) {
|
||||
if (!before_write_callback.is_null())
|
||||
std::move(before_write_callback).Run();
|
||||
|
||||
bool result =
|
||||
ImportantFileWriter::WriteFileAtomically(path, *data, histogram_suffix);
|
||||
|
||||
if (!after_write_callback.is_null())
|
||||
std::move(after_write_callback).Run(result);
|
||||
}
|
||||
|
||||
void DeleteTmpFile(const FilePath& tmp_file_path,
|
||||
StringPiece histogram_suffix) {
|
||||
if (!DeleteFile(tmp_file_path, false)) {
|
||||
UmaHistogramExactLinearWithSuffix(
|
||||
"ImportantFile.FileDeleteError", histogram_suffix,
|
||||
-base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
|
||||
StringPiece data,
|
||||
StringPiece histogram_suffix) {
|
||||
#if defined(OS_WIN) && DCHECK_IS_ON()
|
||||
// In https://crbug.com/920174, we have cases where CreateTemporaryFileInDir
|
||||
// hits a DCHECK because creation fails with no indication why. Pull the path
|
||||
// onto the stack so that we can see if it is malformed in some odd way.
|
||||
wchar_t path_copy[MAX_PATH];
|
||||
base::wcslcpy(path_copy, path.value().c_str(), base::size(path_copy));
|
||||
base::debug::Alias(path_copy);
|
||||
#endif // defined(OS_WIN) && DCHECK_IS_ON()
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
// On Chrome OS, chrome gets killed when it cannot finish shutdown quickly,
|
||||
// and this function seems to be one of the slowest shutdown steps.
|
||||
// Include some info to the report for investigation. crbug.com/418627
|
||||
// TODO(hashimoto): Remove this.
|
||||
struct {
|
||||
size_t data_size;
|
||||
char path[128];
|
||||
} file_info;
|
||||
file_info.data_size = data.size();
|
||||
strlcpy(file_info.path, path.value().c_str(), base::size(file_info.path));
|
||||
debug::Alias(&file_info);
|
||||
#endif
|
||||
|
||||
// Write the data to a temp file then rename to avoid data loss if we crash
|
||||
// while writing the file. Ensure that the temp file is on the same volume
|
||||
// as target file, so it can be moved in one step, and that the temp file
|
||||
// is securely created.
|
||||
FilePath tmp_file_path;
|
||||
if (!CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
|
||||
const auto last_file_error = base::File::GetLastFileError();
|
||||
UmaHistogramExactLinearWithSuffix("ImportantFile.FileCreateError",
|
||||
histogram_suffix, -last_file_error,
|
||||
-base::File::FILE_ERROR_MAX);
|
||||
LogFailure(path, histogram_suffix, FAILED_CREATING,
|
||||
"could not create temporary file");
|
||||
return false;
|
||||
}
|
||||
|
||||
File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE);
|
||||
if (!tmp_file.IsValid()) {
|
||||
UmaHistogramExactLinearWithSuffix(
|
||||
"ImportantFile.FileOpenError", histogram_suffix,
|
||||
-tmp_file.error_details(), -base::File::FILE_ERROR_MAX);
|
||||
LogFailure(path, histogram_suffix, FAILED_OPENING,
|
||||
"could not open temporary file");
|
||||
DeleteFile(tmp_file_path, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this fails in the wild, something really bad is going on.
|
||||
const int data_length = checked_cast<int32_t>(data.length());
|
||||
int bytes_written = tmp_file.Write(0, data.data(), data_length);
|
||||
if (bytes_written < data_length) {
|
||||
UmaHistogramExactLinearWithSuffix(
|
||||
"ImportantFile.FileWriteError", histogram_suffix,
|
||||
-base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX);
|
||||
}
|
||||
bool flush_success = tmp_file.Flush();
|
||||
tmp_file.Close();
|
||||
|
||||
if (bytes_written < data_length) {
|
||||
LogFailure(path, histogram_suffix, FAILED_WRITING,
|
||||
"error writing, bytes_written=" + NumberToString(bytes_written));
|
||||
DeleteTmpFile(tmp_file_path, histogram_suffix);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!flush_success) {
|
||||
LogFailure(path, histogram_suffix, FAILED_FLUSHING, "error flushing");
|
||||
DeleteTmpFile(tmp_file_path, histogram_suffix);
|
||||
return false;
|
||||
}
|
||||
|
||||
base::File::Error replace_file_error = base::File::FILE_OK;
|
||||
if (!ReplaceFile(tmp_file_path, path, &replace_file_error)) {
|
||||
UmaHistogramExactLinearWithSuffix("ImportantFile.FileRenameError",
|
||||
histogram_suffix, -replace_file_error,
|
||||
-base::File::FILE_ERROR_MAX);
|
||||
LogFailure(path, histogram_suffix, FAILED_RENAMING,
|
||||
"could not rename temporary file");
|
||||
DeleteTmpFile(tmp_file_path, histogram_suffix);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ImportantFileWriter::ImportantFileWriter(
|
||||
const FilePath& path,
|
||||
scoped_refptr<SequencedTaskRunner> task_runner,
|
||||
const char* histogram_suffix)
|
||||
: ImportantFileWriter(path,
|
||||
std::move(task_runner),
|
||||
kDefaultCommitInterval,
|
||||
histogram_suffix) {}
|
||||
|
||||
ImportantFileWriter::ImportantFileWriter(
|
||||
const FilePath& path,
|
||||
scoped_refptr<SequencedTaskRunner> task_runner,
|
||||
TimeDelta interval,
|
||||
const char* histogram_suffix)
|
||||
: path_(path),
|
||||
task_runner_(std::move(task_runner)),
|
||||
serializer_(nullptr),
|
||||
commit_interval_(interval),
|
||||
histogram_suffix_(histogram_suffix ? histogram_suffix : "") {
|
||||
DCHECK(task_runner_);
|
||||
}
|
||||
|
||||
ImportantFileWriter::~ImportantFileWriter() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
// We're usually a member variable of some other object, which also tends
|
||||
// to be our serializer. It may not be safe to call back to the parent object
|
||||
// being destructed.
|
||||
DCHECK(!HasPendingWrite());
|
||||
}
|
||||
|
||||
bool ImportantFileWriter::HasPendingWrite() const {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
return timer().IsRunning();
|
||||
}
|
||||
|
||||
void ImportantFileWriter::WriteNow(std::unique_ptr<std::string> data) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (!IsValueInRangeForNumericType<int32_t>(data->length())) {
|
||||
NOTREACHED();
|
||||
return;
|
||||
}
|
||||
|
||||
RepeatingClosure task = AdaptCallbackForRepeating(
|
||||
BindOnce(&WriteScopedStringToFileAtomically, path_, std::move(data),
|
||||
std::move(before_next_write_callback_),
|
||||
std::move(after_next_write_callback_), histogram_suffix_));
|
||||
|
||||
if (!task_runner_->PostTask(FROM_HERE, MakeCriticalClosure(task))) {
|
||||
// Posting the task to background message loop is not expected
|
||||
// to fail, but if it does, avoid losing data and just hit the disk
|
||||
// on the current thread.
|
||||
NOTREACHED();
|
||||
|
||||
std::move(task).Run();
|
||||
}
|
||||
ClearPendingWrite();
|
||||
}
|
||||
|
||||
void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
DCHECK(serializer);
|
||||
serializer_ = serializer;
|
||||
|
||||
if (!timer().IsRunning()) {
|
||||
timer().Start(
|
||||
FROM_HERE, commit_interval_,
|
||||
BindOnce(&ImportantFileWriter::DoScheduledWrite, Unretained(this)));
|
||||
}
|
||||
}
|
||||
|
||||
void ImportantFileWriter::DoScheduledWrite() {
|
||||
DCHECK(serializer_);
|
||||
std::unique_ptr<std::string> data(new std::string);
|
||||
if (serializer_->SerializeData(data.get())) {
|
||||
WriteNow(std::move(data));
|
||||
} else {
|
||||
DLOG(WARNING) << "failed to serialize data to be saved in "
|
||||
<< path_.value();
|
||||
}
|
||||
ClearPendingWrite();
|
||||
}
|
||||
|
||||
void ImportantFileWriter::RegisterOnNextWriteCallbacks(
|
||||
OnceClosure before_next_write_callback,
|
||||
OnceCallback<void(bool success)> after_next_write_callback) {
|
||||
before_next_write_callback_ = std::move(before_next_write_callback);
|
||||
after_next_write_callback_ = std::move(after_next_write_callback);
|
||||
}
|
||||
|
||||
void ImportantFileWriter::ClearPendingWrite() {
|
||||
timer().Stop();
|
||||
serializer_ = nullptr;
|
||||
}
|
||||
|
||||
void ImportantFileWriter::SetTimerForTesting(OneShotTimer* timer_override) {
|
||||
timer_override_ = timer_override;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
161
TMessagesProj/jni/voip/webrtc/base/files/important_file_writer.h
Normal file
161
TMessagesProj/jni/voip/webrtc/base/files/important_file_writer.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_IMPORTANT_FILE_WRITER_H_
|
||||
#define BASE_FILES_IMPORTANT_FILE_WRITER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/timer.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class SequencedTaskRunner;
|
||||
|
||||
// Helper for atomically writing a file to ensure that it won't be corrupted by
|
||||
// *application* crash during write (implemented as create, flush, rename).
|
||||
//
|
||||
// As an added benefit, ImportantFileWriter makes it less likely that the file
|
||||
// is corrupted by *system* crash, though even if the ImportantFileWriter call
|
||||
// has already returned at the time of the crash it is not specified which
|
||||
// version of the file (old or new) is preserved. And depending on system
|
||||
// configuration (hardware and software) a significant likelihood of file
|
||||
// corruption may remain, thus using ImportantFileWriter is not a valid
|
||||
// substitute for file integrity checks and recovery codepaths for malformed
|
||||
// files.
|
||||
//
|
||||
// Also note that ImportantFileWriter can be *really* slow (cf. File::Flush()
|
||||
// for details) and thus please don't block shutdown on ImportantFileWriter.
|
||||
class BASE_EXPORT ImportantFileWriter {
|
||||
public:
|
||||
// Used by ScheduleSave to lazily provide the data to be saved. Allows us
|
||||
// to also batch data serializations.
|
||||
class BASE_EXPORT DataSerializer {
|
||||
public:
|
||||
// Should put serialized string in |data| and return true on successful
|
||||
// serialization. Will be called on the same thread on which
|
||||
// ImportantFileWriter has been created.
|
||||
virtual bool SerializeData(std::string* data) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~DataSerializer() = default;
|
||||
};
|
||||
|
||||
// Save |data| to |path| in an atomic manner. Blocks and writes data on the
|
||||
// current thread. Does not guarantee file integrity across system crash (see
|
||||
// the class comment above).
|
||||
static bool WriteFileAtomically(const FilePath& path,
|
||||
StringPiece data,
|
||||
StringPiece histogram_suffix = StringPiece());
|
||||
|
||||
// Initialize the writer.
|
||||
// |path| is the name of file to write.
|
||||
// |task_runner| is the SequencedTaskRunner instance where on which we will
|
||||
// execute file I/O operations.
|
||||
// All non-const methods, ctor and dtor must be called on the same thread.
|
||||
ImportantFileWriter(const FilePath& path,
|
||||
scoped_refptr<SequencedTaskRunner> task_runner,
|
||||
const char* histogram_suffix = nullptr);
|
||||
|
||||
// Same as above, but with a custom commit interval.
|
||||
ImportantFileWriter(const FilePath& path,
|
||||
scoped_refptr<SequencedTaskRunner> task_runner,
|
||||
TimeDelta interval,
|
||||
const char* histogram_suffix = nullptr);
|
||||
|
||||
// You have to ensure that there are no pending writes at the moment
|
||||
// of destruction.
|
||||
~ImportantFileWriter();
|
||||
|
||||
const FilePath& path() const { return path_; }
|
||||
|
||||
// Returns true if there is a scheduled write pending which has not yet
|
||||
// been started.
|
||||
bool HasPendingWrite() const;
|
||||
|
||||
// Save |data| to target filename. Does not block. If there is a pending write
|
||||
// scheduled by ScheduleWrite(), it is cancelled.
|
||||
void WriteNow(std::unique_ptr<std::string> data);
|
||||
|
||||
// Schedule a save to target filename. Data will be serialized and saved
|
||||
// to disk after the commit interval. If another ScheduleWrite is issued
|
||||
// before that, only one serialization and write to disk will happen, and
|
||||
// the most recent |serializer| will be used. This operation does not block.
|
||||
// |serializer| should remain valid through the lifetime of
|
||||
// ImportantFileWriter.
|
||||
void ScheduleWrite(DataSerializer* serializer);
|
||||
|
||||
// Serialize data pending to be saved and execute write on backend thread.
|
||||
void DoScheduledWrite();
|
||||
|
||||
// Registers |before_next_write_callback| and |after_next_write_callback| to
|
||||
// be synchronously invoked from WriteFileAtomically() before its next write
|
||||
// and after its next write, respectively. The boolean passed to
|
||||
// |after_next_write_callback| indicates whether the write was successful.
|
||||
// Both callbacks must be thread safe as they will be called on |task_runner_|
|
||||
// and may be called during Chrome shutdown.
|
||||
// If called more than once before a write is scheduled on |task_runner|, the
|
||||
// latest callbacks clobber the others.
|
||||
void RegisterOnNextWriteCallbacks(
|
||||
OnceClosure before_next_write_callback,
|
||||
OnceCallback<void(bool success)> after_next_write_callback);
|
||||
|
||||
TimeDelta commit_interval() const {
|
||||
return commit_interval_;
|
||||
}
|
||||
|
||||
// Overrides the timer to use for scheduling writes with |timer_override|.
|
||||
void SetTimerForTesting(OneShotTimer* timer_override);
|
||||
|
||||
private:
|
||||
const OneShotTimer& timer() const {
|
||||
return timer_override_ ? *timer_override_ : timer_;
|
||||
}
|
||||
OneShotTimer& timer() { return timer_override_ ? *timer_override_ : timer_; }
|
||||
|
||||
void ClearPendingWrite();
|
||||
|
||||
// Invoked synchronously on the next write event.
|
||||
OnceClosure before_next_write_callback_;
|
||||
OnceCallback<void(bool success)> after_next_write_callback_;
|
||||
|
||||
// Path being written to.
|
||||
const FilePath path_;
|
||||
|
||||
// TaskRunner for the thread on which file I/O can be done.
|
||||
const scoped_refptr<SequencedTaskRunner> task_runner_;
|
||||
|
||||
// Timer used to schedule commit after ScheduleWrite.
|
||||
OneShotTimer timer_;
|
||||
|
||||
// An override for |timer_| used for testing.
|
||||
OneShotTimer* timer_override_ = nullptr;
|
||||
|
||||
// Serializer which will provide the data to be saved.
|
||||
DataSerializer* serializer_;
|
||||
|
||||
// Time delta after which scheduled data will be written to disk.
|
||||
const TimeDelta commit_interval_;
|
||||
|
||||
// Custom histogram suffix.
|
||||
const std::string histogram_suffix_;
|
||||
|
||||
SEQUENCE_CHECKER(sequence_checker_);
|
||||
|
||||
WeakPtrFactory<ImportantFileWriter> weak_factory_{this};
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ImportantFileWriter);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_IMPORTANT_FILE_WRITER_H_
|
||||
144
TMessagesProj/jni/voip/webrtc/base/files/memory_mapped_file.cc
Normal file
144
TMessagesProj/jni/voip/webrtc/base/files/memory_mapped_file.cc
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/memory_mapped_file.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_math.h"
|
||||
#include "base/system/sys_info.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile = {0, 0};
|
||||
|
||||
bool MemoryMappedFile::Region::operator==(
|
||||
const MemoryMappedFile::Region& other) const {
|
||||
return other.offset == offset && other.size == size;
|
||||
}
|
||||
|
||||
bool MemoryMappedFile::Region::operator!=(
|
||||
const MemoryMappedFile::Region& other) const {
|
||||
return other.offset != offset || other.size != size;
|
||||
}
|
||||
|
||||
MemoryMappedFile::~MemoryMappedFile() {
|
||||
CloseHandles();
|
||||
}
|
||||
|
||||
#if !defined(OS_NACL)
|
||||
bool MemoryMappedFile::Initialize(const FilePath& file_name, Access access) {
|
||||
if (IsValid())
|
||||
return false;
|
||||
|
||||
uint32_t flags = 0;
|
||||
switch (access) {
|
||||
case READ_ONLY:
|
||||
flags = File::FLAG_OPEN | File::FLAG_READ;
|
||||
break;
|
||||
case READ_WRITE:
|
||||
flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE;
|
||||
break;
|
||||
case READ_WRITE_EXTEND:
|
||||
// Can't open with "extend" because no maximum size is known.
|
||||
NOTREACHED();
|
||||
break;
|
||||
#if defined(OS_WIN)
|
||||
case READ_CODE_IMAGE:
|
||||
flags |= File::FLAG_OPEN | File::FLAG_READ | File::FLAG_EXCLUSIVE_WRITE |
|
||||
File::FLAG_EXECUTE;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
file_.Initialize(file_name, flags);
|
||||
|
||||
if (!file_.IsValid()) {
|
||||
DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MapFileRegionToMemory(Region::kWholeFile, access)) {
|
||||
CloseHandles();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryMappedFile::Initialize(File file, Access access) {
|
||||
DCHECK_NE(READ_WRITE_EXTEND, access);
|
||||
return Initialize(std::move(file), Region::kWholeFile, access);
|
||||
}
|
||||
|
||||
bool MemoryMappedFile::Initialize(File file,
|
||||
const Region& region,
|
||||
Access access) {
|
||||
switch (access) {
|
||||
case READ_WRITE_EXTEND:
|
||||
DCHECK(Region::kWholeFile != region);
|
||||
{
|
||||
CheckedNumeric<int64_t> region_end(region.offset);
|
||||
region_end += region.size;
|
||||
if (!region_end.IsValid()) {
|
||||
DLOG(ERROR) << "Region bounds exceed maximum for base::File.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
FALLTHROUGH;
|
||||
case READ_ONLY:
|
||||
case READ_WRITE:
|
||||
// Ensure that the region values are valid.
|
||||
if (region.offset < 0) {
|
||||
DLOG(ERROR) << "Region bounds are not valid.";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
#if defined(OS_WIN)
|
||||
case READ_CODE_IMAGE:
|
||||
// Can't open with "READ_CODE_IMAGE", not supported outside Windows
|
||||
// or with a |region|.
|
||||
NOTREACHED();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (IsValid())
|
||||
return false;
|
||||
|
||||
if (region != Region::kWholeFile)
|
||||
DCHECK_GE(region.offset, 0);
|
||||
|
||||
file_ = std::move(file);
|
||||
|
||||
if (!MapFileRegionToMemory(region, access)) {
|
||||
CloseHandles();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryMappedFile::IsValid() const {
|
||||
return data_ != nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
void MemoryMappedFile::CalculateVMAlignedBoundaries(int64_t start,
|
||||
size_t size,
|
||||
int64_t* aligned_start,
|
||||
size_t* aligned_size,
|
||||
int32_t* offset) {
|
||||
// Sadly, on Windows, the mmap alignment is not just equal to the page size.
|
||||
auto mask = SysInfo::VMAllocationGranularity() - 1;
|
||||
DCHECK(IsValueInRangeForNumericType<int32_t>(mask));
|
||||
*offset = start & mask;
|
||||
*aligned_start = start & ~mask;
|
||||
*aligned_size = (size + *offset + mask) & ~mask;
|
||||
}
|
||||
#endif // !defined(OS_NACL)
|
||||
|
||||
} // namespace base
|
||||
152
TMessagesProj/jni/voip/webrtc/base/files/memory_mapped_file.h
Normal file
152
TMessagesProj/jni/voip/webrtc/base/files/memory_mapped_file.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_MEMORY_MAPPED_FILE_H_
|
||||
#define BASE_FILES_MEMORY_MAPPED_FILE_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/macros.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
class FilePath;
|
||||
|
||||
class BASE_EXPORT MemoryMappedFile {
|
||||
public:
|
||||
enum Access {
|
||||
// Mapping a file into memory effectively allows for file I/O on any thread.
|
||||
// The accessing thread could be paused while data from the file is paged
|
||||
// into memory. Worse, a corrupted filesystem could cause a SEGV within the
|
||||
// program instead of just an I/O error.
|
||||
READ_ONLY,
|
||||
|
||||
// This provides read/write access to a file and must be used with care of
|
||||
// the additional subtleties involved in doing so. Though the OS will do
|
||||
// the writing of data on its own time, too many dirty pages can cause
|
||||
// the OS to pause the thread while it writes them out. The pause can
|
||||
// be as much as 1s on some systems.
|
||||
READ_WRITE,
|
||||
|
||||
// This provides read/write access but with the ability to write beyond
|
||||
// the end of the existing file up to a maximum size specified as the
|
||||
// "region". Depending on the OS, the file may or may not be immediately
|
||||
// extended to the maximum size though it won't be loaded in RAM until
|
||||
// needed. Note, however, that the maximum size will still be reserved
|
||||
// in the process address space.
|
||||
READ_WRITE_EXTEND,
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// This provides read access, but as executable code used for prefetching
|
||||
// DLLs into RAM to avoid inefficient hard fault patterns such as during
|
||||
// process startup. The accessing thread could be paused while data from
|
||||
// the file is read into memory (if needed).
|
||||
READ_CODE_IMAGE,
|
||||
#endif
|
||||
};
|
||||
|
||||
// The default constructor sets all members to invalid/null values.
|
||||
MemoryMappedFile();
|
||||
~MemoryMappedFile();
|
||||
|
||||
// Used to hold information about a region [offset + size] of a file.
|
||||
struct BASE_EXPORT Region {
|
||||
static const Region kWholeFile;
|
||||
|
||||
bool operator==(const Region& other) const;
|
||||
bool operator!=(const Region& other) const;
|
||||
|
||||
// Start of the region (measured in bytes from the beginning of the file).
|
||||
int64_t offset;
|
||||
|
||||
// Length of the region in bytes.
|
||||
size_t size;
|
||||
};
|
||||
|
||||
// Opens an existing file and maps it into memory. |access| can be read-only
|
||||
// or read/write but not read/write+extend. If this object already points
|
||||
// to a valid memory mapped file then this method will fail and return
|
||||
// false. If it cannot open the file, the file does not exist, or the
|
||||
// memory mapping fails, it will return false.
|
||||
WARN_UNUSED_RESULT bool Initialize(const FilePath& file_name, Access access);
|
||||
WARN_UNUSED_RESULT bool Initialize(const FilePath& file_name) {
|
||||
return Initialize(file_name, READ_ONLY);
|
||||
}
|
||||
|
||||
// As above, but works with an already-opened file. |access| can be read-only
|
||||
// or read/write but not read/write+extend. MemoryMappedFile takes ownership
|
||||
// of |file| and closes it when done. |file| must have been opened with
|
||||
// permissions suitable for |access|. If the memory mapping fails, it will
|
||||
// return false.
|
||||
WARN_UNUSED_RESULT bool Initialize(File file, Access access);
|
||||
WARN_UNUSED_RESULT bool Initialize(File file) {
|
||||
return Initialize(std::move(file), READ_ONLY);
|
||||
}
|
||||
|
||||
// As above, but works with a region of an already-opened file. |access|
|
||||
// must not be READ_CODE_IMAGE. If READ_WRITE_EXTEND is specified then
|
||||
// |region| provides the maximum size of the file. If the memory mapping
|
||||
// fails, it return false.
|
||||
WARN_UNUSED_RESULT bool Initialize(File file,
|
||||
const Region& region,
|
||||
Access access);
|
||||
WARN_UNUSED_RESULT bool Initialize(File file, const Region& region) {
|
||||
return Initialize(std::move(file), region, READ_ONLY);
|
||||
}
|
||||
|
||||
const uint8_t* data() const { return data_; }
|
||||
uint8_t* data() { return data_; }
|
||||
size_t length() const { return length_; }
|
||||
|
||||
// Is file_ a valid file handle that points to an open, memory mapped file?
|
||||
bool IsValid() const;
|
||||
|
||||
private:
|
||||
// Given the arbitrarily aligned memory region [start, size], returns the
|
||||
// boundaries of the region aligned to the granularity specified by the OS,
|
||||
// (a page on Linux, ~32k on Windows) as follows:
|
||||
// - |aligned_start| is page aligned and <= |start|.
|
||||
// - |aligned_size| is a multiple of the VM granularity and >= |size|.
|
||||
// - |offset| is the displacement of |start| w.r.t |aligned_start|.
|
||||
static void CalculateVMAlignedBoundaries(int64_t start,
|
||||
size_t size,
|
||||
int64_t* aligned_start,
|
||||
size_t* aligned_size,
|
||||
int32_t* offset);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Maps the executable file to memory, set |data_| to that memory address.
|
||||
// Return true on success.
|
||||
bool MapImageToMemory(Access access);
|
||||
#endif
|
||||
|
||||
// Map the file to memory, set data_ to that memory address. Return true on
|
||||
// success, false on any kind of failure. This is a helper for Initialize().
|
||||
bool MapFileRegionToMemory(const Region& region, Access access);
|
||||
|
||||
// Closes all open handles.
|
||||
void CloseHandles();
|
||||
|
||||
File file_;
|
||||
uint8_t* data_;
|
||||
size_t length_;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
win::ScopedHandle file_mapping_;
|
||||
#endif
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_MEMORY_MAPPED_FILE_H_
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/memory_mapped_file.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
MemoryMappedFile::MemoryMappedFile() : data_(nullptr), length_(0) {}
|
||||
|
||||
#if !defined(OS_NACL)
|
||||
bool MemoryMappedFile::MapFileRegionToMemory(
|
||||
const MemoryMappedFile::Region& region,
|
||||
Access access) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
|
||||
off_t map_start = 0;
|
||||
size_t map_size = 0;
|
||||
int32_t data_offset = 0;
|
||||
|
||||
if (region == MemoryMappedFile::Region::kWholeFile) {
|
||||
int64_t file_len = file_.GetLength();
|
||||
if (file_len < 0) {
|
||||
DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
|
||||
return false;
|
||||
}
|
||||
if (!IsValueInRangeForNumericType<size_t>(file_len))
|
||||
return false;
|
||||
map_size = static_cast<size_t>(file_len);
|
||||
length_ = map_size;
|
||||
} else {
|
||||
// The region can be arbitrarily aligned. mmap, instead, requires both the
|
||||
// start and size to be page-aligned. Hence, we map here the page-aligned
|
||||
// outer region [|aligned_start|, |aligned_start| + |size|] which contains
|
||||
// |region| and then add up the |data_offset| displacement.
|
||||
int64_t aligned_start = 0;
|
||||
size_t aligned_size = 0;
|
||||
CalculateVMAlignedBoundaries(region.offset,
|
||||
region.size,
|
||||
&aligned_start,
|
||||
&aligned_size,
|
||||
&data_offset);
|
||||
|
||||
// Ensure that the casts in the mmap call below are sane.
|
||||
if (aligned_start < 0 ||
|
||||
!IsValueInRangeForNumericType<off_t>(aligned_start)) {
|
||||
DLOG(ERROR) << "Region bounds are not valid for mmap";
|
||||
return false;
|
||||
}
|
||||
|
||||
map_start = static_cast<off_t>(aligned_start);
|
||||
map_size = aligned_size;
|
||||
length_ = region.size;
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
switch (access) {
|
||||
case READ_ONLY:
|
||||
flags |= PROT_READ;
|
||||
break;
|
||||
|
||||
case READ_WRITE:
|
||||
flags |= PROT_READ | PROT_WRITE;
|
||||
break;
|
||||
|
||||
case READ_WRITE_EXTEND:
|
||||
flags |= PROT_READ | PROT_WRITE;
|
||||
|
||||
if (!AllocateFileRegion(&file_, region.offset, region.size))
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
data_ = static_cast<uint8_t*>(mmap(nullptr, map_size, flags, MAP_SHARED,
|
||||
file_.GetPlatformFile(), map_start));
|
||||
if (data_ == MAP_FAILED) {
|
||||
DPLOG(ERROR) << "mmap " << file_.GetPlatformFile();
|
||||
return false;
|
||||
}
|
||||
|
||||
data_ += data_offset;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void MemoryMappedFile::CloseHandles() {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
|
||||
if (data_ != nullptr)
|
||||
munmap(data_, length_);
|
||||
file_.Close();
|
||||
|
||||
data_ = nullptr;
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/memory_mapped_file.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "base/win/pe_image.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <winnt.h> // NOLINT(build/include_order)
|
||||
|
||||
namespace base {
|
||||
|
||||
MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0) {
|
||||
}
|
||||
|
||||
bool MemoryMappedFile::MapImageToMemory(Access access) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
|
||||
// The arguments to the calls of ::CreateFile(), ::CreateFileMapping(), and
|
||||
// ::MapViewOfFile() need to be self consistent as far as access rights and
|
||||
// type of mapping or one or more of them will fail in non-obvious ways.
|
||||
|
||||
if (!file_.IsValid())
|
||||
return false;
|
||||
|
||||
file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), nullptr,
|
||||
PAGE_READONLY | SEC_IMAGE_NO_EXECUTE, 0,
|
||||
0, NULL));
|
||||
if (!file_mapping_.IsValid())
|
||||
return false;
|
||||
|
||||
data_ = static_cast<uint8_t*>(
|
||||
::MapViewOfFile(file_mapping_.Get(), FILE_MAP_READ, 0, 0, 0));
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
// We need to know how large the mapped file is in some cases
|
||||
|
||||
base::win::PEImage pe_image(data_);
|
||||
length_ = pe_image.GetNTHeaders()->OptionalHeader.SizeOfImage;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryMappedFile::MapFileRegionToMemory(
|
||||
const MemoryMappedFile::Region& region,
|
||||
Access access) {
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
|
||||
DCHECK(access != READ_CODE_IMAGE || region == Region::kWholeFile);
|
||||
|
||||
if (!file_.IsValid())
|
||||
return false;
|
||||
|
||||
int flags = 0;
|
||||
ULARGE_INTEGER size = {};
|
||||
switch (access) {
|
||||
case READ_ONLY:
|
||||
flags |= PAGE_READONLY;
|
||||
break;
|
||||
case READ_WRITE:
|
||||
flags |= PAGE_READWRITE;
|
||||
break;
|
||||
case READ_WRITE_EXTEND:
|
||||
flags |= PAGE_READWRITE;
|
||||
size.QuadPart = region.size;
|
||||
break;
|
||||
case READ_CODE_IMAGE:
|
||||
return MapImageToMemory(access);
|
||||
}
|
||||
|
||||
file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), NULL, flags,
|
||||
size.HighPart, size.LowPart, NULL));
|
||||
if (!file_mapping_.IsValid())
|
||||
return false;
|
||||
|
||||
LARGE_INTEGER map_start = {};
|
||||
SIZE_T map_size = 0;
|
||||
int32_t data_offset = 0;
|
||||
|
||||
if (region == MemoryMappedFile::Region::kWholeFile) {
|
||||
DCHECK_NE(READ_WRITE_EXTEND, access);
|
||||
int64_t file_len = file_.GetLength();
|
||||
if (file_len <= 0 || !IsValueInRangeForNumericType<size_t>(file_len))
|
||||
return false;
|
||||
length_ = static_cast<size_t>(file_len);
|
||||
} else {
|
||||
// The region can be arbitrarily aligned. MapViewOfFile, instead, requires
|
||||
// that the start address is aligned to the VM granularity (which is
|
||||
// typically larger than a page size, for instance 32k).
|
||||
// Also, conversely to POSIX's mmap, the |map_size| doesn't have to be
|
||||
// aligned and must be less than or equal the mapped file size.
|
||||
// We map here the outer region [|aligned_start|, |aligned_start+size|]
|
||||
// which contains |region| and then add up the |data_offset| displacement.
|
||||
int64_t aligned_start = 0;
|
||||
size_t ignored = 0U;
|
||||
CalculateVMAlignedBoundaries(region.offset, region.size, &aligned_start,
|
||||
&ignored, &data_offset);
|
||||
int64_t full_map_size = region.size + data_offset;
|
||||
|
||||
// Ensure that the casts below in the MapViewOfFile call are sane.
|
||||
if (aligned_start < 0 || full_map_size < 0 ||
|
||||
!IsValueInRangeForNumericType<SIZE_T>(
|
||||
static_cast<uint64_t>(full_map_size))) {
|
||||
DLOG(ERROR) << "Region bounds are not valid for MapViewOfFile";
|
||||
return false;
|
||||
}
|
||||
map_start.QuadPart = aligned_start;
|
||||
map_size = static_cast<SIZE_T>(full_map_size);
|
||||
length_ = region.size;
|
||||
}
|
||||
|
||||
data_ = static_cast<uint8_t*>(
|
||||
::MapViewOfFile(file_mapping_.Get(),
|
||||
(flags & PAGE_READONLY) ? FILE_MAP_READ : FILE_MAP_WRITE,
|
||||
map_start.HighPart, map_start.LowPart, map_size));
|
||||
if (data_ == NULL)
|
||||
return false;
|
||||
data_ += data_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MemoryMappedFile::CloseHandles() {
|
||||
if (data_)
|
||||
::UnmapViewOfFile(data_);
|
||||
if (file_mapping_.IsValid())
|
||||
file_mapping_.Close();
|
||||
if (file_.IsValid())
|
||||
file_.Close();
|
||||
|
||||
data_ = NULL;
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
43
TMessagesProj/jni/voip/webrtc/base/files/platform_file.h
Normal file
43
TMessagesProj/jni/voip/webrtc/base/files/platform_file.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_PLATFORM_FILE_H_
|
||||
#define BASE_FILES_PLATFORM_FILE_H_
|
||||
|
||||
#include "base/files/scoped_file.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/windows_types.h"
|
||||
#endif
|
||||
|
||||
// This file defines platform-independent types for dealing with
|
||||
// platform-dependent files. If possible, use the higher-level base::File class
|
||||
// rather than these primitives.
|
||||
|
||||
namespace base {
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
using PlatformFile = HANDLE;
|
||||
using ScopedPlatformFile = ::base::win::ScopedHandle;
|
||||
|
||||
// It would be nice to make this constexpr but INVALID_HANDLE_VALUE is a
|
||||
// ((void*)(-1)) which Clang rejects since reinterpret_cast is technically
|
||||
// disallowed in constexpr. Visual Studio accepts this, however.
|
||||
const PlatformFile kInvalidPlatformFile = INVALID_HANDLE_VALUE;
|
||||
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
|
||||
using PlatformFile = int;
|
||||
using ScopedPlatformFile = ::base::ScopedFD;
|
||||
|
||||
constexpr PlatformFile kInvalidPlatformFile = -1;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // BASE_FILES_PLATFORM_FILE_H_
|
||||
49
TMessagesProj/jni/voip/webrtc/base/files/scoped_file.cc
Normal file
49
TMessagesProj/jni/voip/webrtc/base/files/scoped_file.cc
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/scoped_file.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
|
||||
// static
|
||||
void ScopedFDCloseTraits::Free(int fd) {
|
||||
// It's important to crash here.
|
||||
// There are security implications to not closing a file descriptor
|
||||
// properly. As file descriptors are "capabilities", keeping them open
|
||||
// would make the current process keep access to a resource. Much of
|
||||
// Chrome relies on being able to "drop" such access.
|
||||
// It's especially problematic on Linux with the setuid sandbox, where
|
||||
// a single open directory would bypass the entire security model.
|
||||
int ret = IGNORE_EINTR(close(fd));
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FUCHSIA) || \
|
||||
defined(OS_ANDROID)
|
||||
// NB: Some file descriptors can return errors from close() e.g. network
|
||||
// filesystems such as NFS and Linux input devices. On Linux, macOS, and
|
||||
// Fuchsia's POSIX layer, errors from close other than EBADF do not indicate
|
||||
// failure to actually close the fd.
|
||||
if (ret != 0 && errno != EBADF)
|
||||
ret = 0;
|
||||
#endif
|
||||
|
||||
PCHECK(0 == ret);
|
||||
}
|
||||
|
||||
#endif // OS_POSIX || OS_FUCHSIA
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
70
TMessagesProj/jni/voip/webrtc/base/files/scoped_file.h
Normal file
70
TMessagesProj/jni/voip/webrtc/base/files/scoped_file.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_SCOPED_FILE_H_
|
||||
#define BASE_FILES_SCOPED_FILE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/scoped_generic.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace internal {
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
// Use fdsan on android.
|
||||
struct BASE_EXPORT ScopedFDCloseTraits : public ScopedGenericOwnershipTracking {
|
||||
static int InvalidValue() { return -1; }
|
||||
static void Free(int);
|
||||
static void Acquire(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);
|
||||
static void Release(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);
|
||||
};
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
struct BASE_EXPORT ScopedFDCloseTraits {
|
||||
static int InvalidValue() {
|
||||
return -1;
|
||||
}
|
||||
static void Free(int fd);
|
||||
};
|
||||
#endif
|
||||
|
||||
// Functor for |ScopedFILE| (below).
|
||||
struct ScopedFILECloser {
|
||||
inline void operator()(FILE* x) const {
|
||||
if (x)
|
||||
fclose(x);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// A low-level Posix file descriptor closer class. Use this when writing
|
||||
// platform-specific code, especially that does non-file-like things with the
|
||||
// FD (like sockets).
|
||||
//
|
||||
// If you're writing low-level Windows code, see base/win/scoped_handle.h
|
||||
// which provides some additional functionality.
|
||||
//
|
||||
// If you're writing cross-platform code that deals with actual files, you
|
||||
// should generally use base::File instead which can be constructed with a
|
||||
// handle, and in addition to handling ownership, has convenient cross-platform
|
||||
// file manipulation functions on it.
|
||||
typedef ScopedGeneric<int, internal::ScopedFDCloseTraits> ScopedFD;
|
||||
#endif
|
||||
|
||||
// Automatically closes |FILE*|s.
|
||||
typedef std::unique_ptr<FILE, internal::ScopedFILECloser> ScopedFILE;
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_SCOPED_FILE_H_
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/scoped_file.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Copied from <android/fdsan.h>.
|
||||
// This can go away once this header is included in our copy of the NDK.
|
||||
extern "C" {
|
||||
void android_fdsan_exchange_owner_tag(int fd,
|
||||
uint64_t expected_tag,
|
||||
uint64_t new_tag)
|
||||
__attribute__((__weak__));
|
||||
}
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
static uint64_t ScopedFDToTag(const ScopedFD& owner) {
|
||||
return reinterpret_cast<uint64_t>(&owner);
|
||||
}
|
||||
|
||||
// static
|
||||
void ScopedFDCloseTraits::Acquire(const ScopedFD& owner, int fd) {
|
||||
if (android_fdsan_exchange_owner_tag) {
|
||||
android_fdsan_exchange_owner_tag(fd, 0, ScopedFDToTag(owner));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void ScopedFDCloseTraits::Release(const ScopedFD& owner, int fd) {
|
||||
if (android_fdsan_exchange_owner_tag) {
|
||||
android_fdsan_exchange_owner_tag(fd, ScopedFDToTag(owner), 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
106
TMessagesProj/jni/voip/webrtc/base/files/scoped_temp_dir.cc
Normal file
106
TMessagesProj/jni/voip/webrtc/base/files/scoped_temp_dir.cc
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/files/scoped_temp_dir.h"
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr FilePath::CharType kScopedDirPrefix[] =
|
||||
FILE_PATH_LITERAL("scoped_dir");
|
||||
|
||||
} // namespace
|
||||
|
||||
ScopedTempDir::ScopedTempDir() = default;
|
||||
|
||||
ScopedTempDir::ScopedTempDir(ScopedTempDir&& other) noexcept
|
||||
: path_(other.Take()) {}
|
||||
|
||||
ScopedTempDir& ScopedTempDir::operator=(ScopedTempDir&& other) {
|
||||
if (!path_.empty() && !Delete())
|
||||
DLOG(WARNING) << "Could not delete temp dir in operator=().";
|
||||
path_ = other.Take();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ScopedTempDir::~ScopedTempDir() {
|
||||
if (!path_.empty() && !Delete())
|
||||
DLOG(WARNING) << "Could not delete temp dir in dtor.";
|
||||
}
|
||||
|
||||
bool ScopedTempDir::CreateUniqueTempDir() {
|
||||
if (!path_.empty())
|
||||
return false;
|
||||
|
||||
// This "scoped_dir" prefix is only used on Windows and serves as a template
|
||||
// for the unique name.
|
||||
if (!CreateNewTempDirectory(kScopedDirPrefix, &path_))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) {
|
||||
if (!path_.empty())
|
||||
return false;
|
||||
|
||||
// If |base_path| does not exist, create it.
|
||||
if (!CreateDirectory(base_path))
|
||||
return false;
|
||||
|
||||
// Create a new, uniquely named directory under |base_path|.
|
||||
if (!CreateTemporaryDirInDir(base_path, kScopedDirPrefix, &path_))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopedTempDir::Set(const FilePath& path) {
|
||||
if (!path_.empty())
|
||||
return false;
|
||||
|
||||
if (!DirectoryExists(path) && !CreateDirectory(path))
|
||||
return false;
|
||||
|
||||
path_ = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopedTempDir::Delete() {
|
||||
if (path_.empty())
|
||||
return false;
|
||||
|
||||
bool ret = DeleteFileRecursively(path_);
|
||||
if (ret) {
|
||||
// We only clear the path if deleted the directory.
|
||||
path_.clear();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
FilePath ScopedTempDir::Take() {
|
||||
return std::exchange(path_, FilePath());
|
||||
}
|
||||
|
||||
const FilePath& ScopedTempDir::GetPath() const {
|
||||
DCHECK(!path_.empty()) << "Did you call CreateUniqueTempDir* before?";
|
||||
return path_;
|
||||
}
|
||||
|
||||
bool ScopedTempDir::IsValid() const {
|
||||
return !path_.empty() && DirectoryExists(path_);
|
||||
}
|
||||
|
||||
// static
|
||||
const FilePath::CharType* ScopedTempDir::GetTempDirPrefix() {
|
||||
return kScopedDirPrefix;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
72
TMessagesProj/jni/voip/webrtc/base/files/scoped_temp_dir.h
Normal file
72
TMessagesProj/jni/voip/webrtc/base/files/scoped_temp_dir.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_FILES_SCOPED_TEMP_DIR_H_
|
||||
#define BASE_FILES_SCOPED_TEMP_DIR_H_
|
||||
|
||||
// An object representing a temporary / scratch directory that should be
|
||||
// cleaned up (recursively) when this object goes out of scope. Since deletion
|
||||
// occurs during the destructor, no further error handling is possible if the
|
||||
// directory fails to be deleted. As a result, deletion is not guaranteed by
|
||||
// this class. (However note that, whenever possible, by default
|
||||
// CreateUniqueTempDir creates the directory in a location that is
|
||||
// automatically cleaned up on reboot, or at other appropriate times.)
|
||||
//
|
||||
// Multiple calls to the methods which establish a temporary directory
|
||||
// (CreateUniqueTempDir, CreateUniqueTempDirUnderPath, and Set) must have
|
||||
// intervening calls to Delete or Take, or the calls will fail.
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/files/file_path.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class BASE_EXPORT ScopedTempDir {
|
||||
public:
|
||||
// No directory is owned/created initially.
|
||||
ScopedTempDir();
|
||||
|
||||
ScopedTempDir(ScopedTempDir&&) noexcept;
|
||||
ScopedTempDir& operator=(ScopedTempDir&&);
|
||||
|
||||
// Recursively delete path.
|
||||
~ScopedTempDir();
|
||||
|
||||
// Creates a unique directory in TempPath, and takes ownership of it.
|
||||
// See file_util::CreateNewTemporaryDirectory.
|
||||
bool CreateUniqueTempDir() WARN_UNUSED_RESULT;
|
||||
|
||||
// Creates a unique directory under a given path, and takes ownership of it.
|
||||
bool CreateUniqueTempDirUnderPath(const FilePath& path) WARN_UNUSED_RESULT;
|
||||
|
||||
// Takes ownership of directory at |path|, creating it if necessary.
|
||||
// Don't call multiple times unless Take() has been called first.
|
||||
bool Set(const FilePath& path) WARN_UNUSED_RESULT;
|
||||
|
||||
// Deletes the temporary directory wrapped by this object.
|
||||
bool Delete() WARN_UNUSED_RESULT;
|
||||
|
||||
// Caller takes ownership of the temporary directory so it won't be destroyed
|
||||
// when this object goes out of scope.
|
||||
FilePath Take();
|
||||
|
||||
// Returns the path to the created directory. Call one of the
|
||||
// CreateUniqueTempDir* methods before getting the path.
|
||||
const FilePath& GetPath() const;
|
||||
|
||||
// Returns true if path_ is non-empty and exists.
|
||||
bool IsValid() const;
|
||||
|
||||
// Returns the prefix used for temp directory names generated by
|
||||
// ScopedTempDirs.
|
||||
static const FilePath::CharType* GetTempDirPrefix();
|
||||
|
||||
private:
|
||||
FilePath path_;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_FILES_SCOPED_TEMP_DIR_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue