summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authordumi@chromium.org <dumi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-10 03:01:14 +0000
committerdumi@chromium.org <dumi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-10 03:01:14 +0000
commit3fb43ed1ad7bcfeb686aa48e296ffbc639b35ad7 (patch)
tree652cdff2f723df25189f248740a310dc10583460 /base
parent2758379366b4aafde0bce40b7d619840080b5387 (diff)
downloadchromium_src-3fb43ed1ad7bcfeb686aa48e296ffbc639b35ad7.zip
chromium_src-3fb43ed1ad7bcfeb686aa48e296ffbc639b35ad7.tar.gz
chromium_src-3fb43ed1ad7bcfeb686aa48e296ffbc639b35ad7.tar.bz2
Adding some functions that operate on file handles.
TEST=platform_file_unittest.cc BUG=none TBR=darin Review URL: http://codereview.chromium.org/3354020 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@59041 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/base.gyp1
-rw-r--r--base/file_util_proxy.cc230
-rw-r--r--base/file_util_proxy.h51
-rw-r--r--base/platform_file.h28
-rw-r--r--base/platform_file_posix.cc138
-rw-r--r--base/platform_file_unittest.cc315
-rw-r--r--base/platform_file_win.cc149
-rw-r--r--base/time.h1
8 files changed, 857 insertions, 56 deletions
diff --git a/base/base.gyp b/base/base.gyp
index a7a7a1a..5ab2928 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -113,6 +113,7 @@
'path_service_unittest.cc',
'pe_image_unittest.cc',
'pickle_unittest.cc',
+ 'platform_file_unittest.cc',
'platform_thread_unittest.cc',
'pr_time_unittest.cc',
'process_util_unittest.cc',
diff --git a/base/file_util_proxy.cc b/base/file_util_proxy.cc
index 6e4222d..941dede 100644
--- a/base/file_util_proxy.cc
+++ b/base/file_util_proxy.cc
@@ -429,6 +429,173 @@ class RelayGetFileInfo : public MessageLoopRelay {
base::PlatformFileInfo file_info_;
};
+class RelayGetFileInfoFromPlatformFile : public MessageLoopRelay {
+ public:
+ RelayGetFileInfoFromPlatformFile(
+ base::PlatformFile file,
+ base::FileUtilProxy::GetFileInfoCallback* callback)
+ : callback_(callback),
+ file_(file) {
+ DCHECK(callback);
+ }
+
+ protected:
+ virtual void RunWork() {
+ if (!base::GetPlatformFileInfo(file_, &file_info_))
+ set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+ }
+
+ virtual void RunCallback() {
+ callback_->Run(error_code(), file_info_);
+ delete callback_;
+ }
+
+ private:
+ base::FileUtilProxy::GetFileInfoCallback* callback_;
+ base::PlatformFile file_;
+ base::PlatformFileInfo file_info_;
+};
+
+class RelayRead : public MessageLoopRelay {
+ public:
+ RelayRead(base::PlatformFile file,
+ int64 offset,
+ char* buffer,
+ int bytes_to_read,
+ base::FileUtilProxy::ReadWriteCallback* callback)
+ : file_(file),
+ offset_(offset),
+ buffer_(buffer),
+ bytes_to_read_(bytes_to_read),
+ callback_(callback),
+ bytes_read_(0) {
+ }
+
+ protected:
+ virtual void RunWork() {
+ bytes_read_ = base::ReadPlatformFile(file_, offset_, buffer_,
+ bytes_to_read_);
+ if (bytes_read_ < 0)
+ set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+ }
+
+ virtual void RunCallback() {
+ if (callback_) {
+ callback_->Run(error_code(), bytes_read_);
+ delete callback_;
+ }
+ }
+
+ private:
+ base::PlatformFile file_;
+ int64 offset_;
+ char* buffer_;
+ int bytes_to_read_;
+ base::FileUtilProxy::ReadWriteCallback* callback_;
+ int bytes_read_;
+};
+
+class RelayWrite : public MessageLoopRelay {
+ public:
+ RelayWrite(base::PlatformFile file,
+ long long offset,
+ const char* buffer,
+ int bytes_to_write,
+ base::FileUtilProxy::ReadWriteCallback* callback)
+ : file_(file),
+ offset_(offset),
+ buffer_(buffer),
+ bytes_to_write_(bytes_to_write),
+ callback_(callback) {
+ }
+
+ protected:
+ virtual void RunWork() {
+ bytes_written_ = base::WritePlatformFile(file_, offset_, buffer_,
+ bytes_to_write_);
+ if (bytes_written_ < 0)
+ set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+ }
+
+ virtual void RunCallback() {
+ if (callback_) {
+ callback_->Run(error_code(), bytes_written_);
+ delete callback_;
+ }
+ }
+
+ private:
+ base::PlatformFile file_;
+ int64 offset_;
+ const char* buffer_;
+ int bytes_to_write_;
+ base::FileUtilProxy::ReadWriteCallback* callback_;
+ int bytes_written_;
+};
+
+class RelayTouch : public RelayWithStatusCallback {
+ public:
+ RelayTouch(base::PlatformFile file,
+ const base::Time& last_access_time,
+ const base::Time& last_modified_time,
+ base::FileUtilProxy::StatusCallback* callback)
+ : RelayWithStatusCallback(callback),
+ file_(file),
+ last_access_time_(last_access_time),
+ last_modified_time_(last_modified_time) {
+ }
+
+ protected:
+ virtual void RunWork() {
+ if (!base::TouchPlatformFile(file_, last_access_time_, last_modified_time_))
+ set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+ }
+
+ private:
+ base::PlatformFile file_;
+ base::Time last_access_time_;
+ base::Time last_modified_time_;
+};
+
+class RelayTruncate : public RelayWithStatusCallback {
+ public:
+ RelayTruncate(base::PlatformFile file,
+ int64 length,
+ base::FileUtilProxy::StatusCallback* callback)
+ : RelayWithStatusCallback(callback),
+ file_(file),
+ length_(length) {
+ }
+
+ protected:
+ virtual void RunWork() {
+ if (!base::TruncatePlatformFile(file_, length_))
+ set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+ }
+
+ private:
+ base::PlatformFile file_;
+ int64 length_;
+};
+
+class RelayFlush : public RelayWithStatusCallback {
+ public:
+ RelayFlush(base::PlatformFile file,
+ base::FileUtilProxy::StatusCallback* callback)
+ : RelayWithStatusCallback(callback),
+ file_(file) {
+ }
+
+ protected:
+ virtual void RunWork() {
+ if (!base::FlushPlatformFile(file_))
+ set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+ }
+
+ private:
+ base::PlatformFile file_;
+};
+
bool Start(const tracked_objects::Location& from_here,
scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
scoped_refptr<MessageLoopRelay> relay) {
@@ -529,4 +696,67 @@ bool FileUtilProxy::RecursiveDelete(
new RelayDelete(file_path, true, callback));
}
+// static
+bool FileUtilProxy::GetFileInfoFromPlatformFile(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ PlatformFile file,
+ GetFileInfoCallback* callback) {
+ return Start(FROM_HERE, message_loop_proxy,
+ new RelayGetFileInfoFromPlatformFile(file, callback));
+}
+
+// static
+bool FileUtilProxy::Read(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ PlatformFile file,
+ int64 offset,
+ char* buffer,
+ int bytes_to_read,
+ ReadWriteCallback* callback) {
+ return Start(FROM_HERE, message_loop_proxy,
+ new RelayRead(file, offset, buffer, bytes_to_read, callback));
+}
+
+// static
+bool FileUtilProxy::Write(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ PlatformFile file,
+ int64 offset,
+ const char* buffer,
+ int bytes_to_write,
+ ReadWriteCallback* callback) {
+ return Start(FROM_HERE, message_loop_proxy,
+ new RelayWrite(file, offset, buffer, bytes_to_write, callback));
+}
+
+// static
+bool FileUtilProxy::Touch(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ PlatformFile file,
+ const base::Time& last_access_time,
+ const base::Time& last_modified_time,
+ StatusCallback* callback) {
+ return Start(FROM_HERE, message_loop_proxy,
+ new RelayTouch(file, last_access_time, last_modified_time,
+ callback));
+}
+
+// static
+bool FileUtilProxy::Truncate(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ PlatformFile file,
+ long long length,
+ StatusCallback* callback) {
+ return Start(FROM_HERE, message_loop_proxy,
+ new RelayTruncate(file, length, callback));
+}
+
+// static
+bool FileUtilProxy::Flush(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ PlatformFile file,
+ StatusCallback* callback) {
+ return Start(FROM_HERE, message_loop_proxy, new RelayFlush(file, callback));
+}
+
} // namespace base
diff --git a/base/file_util_proxy.h b/base/file_util_proxy.h
index 929dcd7..581738d 100644
--- a/base/file_util_proxy.h
+++ b/base/file_util_proxy.h
@@ -72,6 +72,11 @@ class FileUtilProxy {
const FilePath& file_path,
GetFileInfoCallback* callback);
+ static bool GetFileInfoFromPlatformFile(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ base::PlatformFile file,
+ GetFileInfoCallback* callback);
+
typedef Callback2<base::PlatformFileError /* error code */,
const std::vector<base::file_util_proxy::Entry>&
>::Type ReadDirectoryCallback;
@@ -119,6 +124,52 @@ class FileUtilProxy {
const FilePath& file_path,
StatusCallback* callback);
+ // Reads from a file. On success, the file pointer is moved to position
+ // |offset + bytes_to_read| in the file. The callback can be NULL.
+ typedef Callback2<base::PlatformFileError /* error code */,
+ int /* bytes read/written */>::Type ReadWriteCallback;
+ static bool Read(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ base::PlatformFile file,
+ int64 offset,
+ char* buffer,
+ int bytes_to_read,
+ ReadWriteCallback* callback);
+
+ // Writes to a file. If |offset| is greater than the length of the file,
+ // |false| is returned. On success, the file pointer is moved to position
+ // |offset + bytes_to_write| in the file. If The callback can be NULL.
+ static bool Write(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ base::PlatformFile file,
+ int64 offset,
+ const char* buffer,
+ int bytes_to_write,
+ ReadWriteCallback* callback);
+
+ // Touches a file. The callback can be NULL.
+ static bool Touch(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ base::PlatformFile file,
+ const base::Time& last_access_time,
+ const base::Time& last_modified_time,
+ StatusCallback* callback);
+
+ // Truncates a file to the given length. If |length| is greater than the
+ // current length of the file, the file will be extended with zeroes.
+ // The callback can be NULL.
+ static bool Truncate(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ base::PlatformFile file,
+ long long length,
+ StatusCallback* callback);
+
+ // Flushes a file. The callback can be NULL.
+ static bool Flush(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy,
+ base::PlatformFile file,
+ StatusCallback* callback);
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(FileUtilProxy);
};
diff --git a/base/platform_file.h b/base/platform_file.h
index 802102c..abe5cdd 100644
--- a/base/platform_file.h
+++ b/base/platform_file.h
@@ -40,7 +40,8 @@ enum PlatformFileFlags {
PLATFORM_FILE_TEMPORARY = 512, // Used on Windows only
PLATFORM_FILE_HIDDEN = 1024, // Used on Windows only
PLATFORM_FILE_DELETE_ON_CLOSE = 2048,
- PLATFORM_FILE_TRUNCATE = 4096
+ PLATFORM_FILE_TRUNCATE = 4096,
+ PLATFORM_FILE_WRITE_ATTRIBUTES = 8192 // Used on Windows only
};
enum PlatformFileError {
@@ -94,6 +95,31 @@ PlatformFile CreatePlatformFile(const std::wstring& name,
// Closes a file handle
bool ClosePlatformFile(PlatformFile file);
+// 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.
+int ReadPlatformFile(PlatformFile file, int64 offset, 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.
+int WritePlatformFile(PlatformFile file, int64 offset,
+ const char* data, int size);
+
+// Truncates the given 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 TruncatePlatformFile(PlatformFile file, int64 length);
+
+// Flushes the buffers of the given file.
+bool FlushPlatformFile(PlatformFile file);
+
+// Touches the given file.
+bool TouchPlatformFile(PlatformFile file, const Time& last_access_time,
+ const Time& last_modified_time);
+
+// Returns some information for the given file.
+bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info);
+
// Use this class to pass ownership of a PlatformFile to a receiver that may or
// may not want to accept it. This class does not own the storage for the
// PlatformFile.
diff --git a/base/platform_file_posix.cc b/base/platform_file_posix.cc
index 1afb250..1d8ef4c 100644
--- a/base/platform_file_posix.cc
+++ b/base/platform_file_posix.cc
@@ -8,12 +8,27 @@
#include <errno.h>
#include <sys/stat.h>
+#include "base/eintr_wrapper.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
namespace base {
+#if defined(OS_OPENBSD) || defined(OS_FREEBSD) || \
+ (defined(OS_MACOSX) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
+typedef struct stat stat_wrapper_t;
+static int CallFstat(int fd, stat_wrapper_t *sb) {
+ return fstat(fd, sb);
+}
+#else
+typedef struct stat64 stat_wrapper_t;
+static int CallFstat(int fd, stat_wrapper_t *sb) {
+ return fstat64(fd, sb);
+}
+#endif
+
// TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here?
PlatformFile CreatePlatformFile(const FilePath& name, int flags,
bool* created, PlatformFileError* error_code) {
@@ -68,41 +83,48 @@ PlatformFile CreatePlatformFile(const FilePath& name, int flags,
}
}
- if ((descriptor < 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) {
+ if (created && (descriptor > 0) && (flags & PLATFORM_FILE_CREATE_ALWAYS))
+ *created = true;
+
+ if ((descriptor > 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) {
unlink(name.value().c_str());
}
- if ((descriptor < 0) && error_code) {
- switch (errno) {
- case EACCES:
- case EISDIR:
- case EROFS:
- case EPERM:
- *error_code = PLATFORM_FILE_ERROR_ACCESS_DENIED;
- break;
- case ETXTBSY:
- *error_code = PLATFORM_FILE_ERROR_IN_USE;
- break;
- case EEXIST:
- *error_code = PLATFORM_FILE_ERROR_EXISTS;
- break;
- case ENOENT:
- *error_code = PLATFORM_FILE_ERROR_NOT_FOUND;
- break;
- case EMFILE:
- *error_code = PLATFORM_FILE_ERROR_TOO_MANY_OPENED;
- break;
- case ENOMEM:
- *error_code = PLATFORM_FILE_ERROR_NO_MEMORY;
- break;
- case ENOSPC:
- *error_code = PLATFORM_FILE_ERROR_NO_SPACE;
- break;
- case ENOTDIR:
- *error_code = PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
- break;
- default:
- *error_code = PLATFORM_FILE_ERROR_FAILED;
+ if (error_code) {
+ if (descriptor >= 0)
+ *error_code = PLATFORM_FILE_OK;
+ else {
+ switch (errno) {
+ case EACCES:
+ case EISDIR:
+ case EROFS:
+ case EPERM:
+ *error_code = PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ break;
+ case ETXTBSY:
+ *error_code = PLATFORM_FILE_ERROR_IN_USE;
+ break;
+ case EEXIST:
+ *error_code = PLATFORM_FILE_ERROR_EXISTS;
+ break;
+ case ENOENT:
+ *error_code = PLATFORM_FILE_ERROR_NOT_FOUND;
+ break;
+ case EMFILE:
+ *error_code = PLATFORM_FILE_ERROR_TOO_MANY_OPENED;
+ break;
+ case ENOMEM:
+ *error_code = PLATFORM_FILE_ERROR_NO_MEMORY;
+ break;
+ case ENOSPC:
+ *error_code = PLATFORM_FILE_ERROR_NO_SPACE;
+ break;
+ case ENOTDIR:
+ *error_code = PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
+ break;
+ default:
+ *error_code = PLATFORM_FILE_ERROR_FAILED;
+ }
}
}
@@ -116,7 +138,57 @@ PlatformFile CreatePlatformFile(const std::wstring& name, int flags,
}
bool ClosePlatformFile(PlatformFile file) {
- return close(file);
+ return !close(file);
+}
+
+int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
+ if (file < 0)
+ return -1;
+
+ return HANDLE_EINTR(pread(file, data, size, offset));
+}
+
+int WritePlatformFile(PlatformFile file, int64 offset,
+ const char* data, int size) {
+ if (file < 0)
+ return -1;
+
+ return HANDLE_EINTR(pwrite(file, data, size, offset));
+}
+
+bool TruncatePlatformFile(PlatformFile file, int64 length) {
+ return ((file >= 0) && !HANDLE_EINTR(ftruncate(file, length)));
+}
+
+bool FlushPlatformFile(PlatformFile file) {
+ return !fsync(file);
+}
+
+bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time,
+ const base::Time& last_modified_time) {
+ if (file < 0)
+ return false;
+
+ timeval times[2];
+ times[0] = last_access_time.ToTimeVal();
+ times[1] = last_modified_time.ToTimeVal();
+ return !futimes(file, times);
+}
+
+bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
+ if (!info)
+ return false;
+
+ stat_wrapper_t file_info;
+ if (CallFstat(file, &file_info))
+ return false;
+
+ info->is_directory = S_ISDIR(file_info.st_mode);
+ info->size = file_info.st_size;
+ info->last_modified = base::Time::FromTimeT(file_info.st_mtime);
+ info->last_accessed = base::Time::FromTimeT(file_info.st_atime);
+ info->creation_time = base::Time::FromTimeT(file_info.st_ctime);
+ return true;
}
} // namespace base
diff --git a/base/platform_file_unittest.cc b/base/platform_file_unittest.cc
new file mode 100644
index 0000000..ef42753
--- /dev/null
+++ b/base/platform_file_unittest.cc
@@ -0,0 +1,315 @@
+// Copyright (c) 2010 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/file_util.h"
+#include "base/platform_file.h"
+#include "base/scoped_temp_dir.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Reads from a file the given number of bytes, or until EOF is reached.
+// Returns the number of bytes read.
+int ReadFully(base::PlatformFile file, int64 offset, char* data, int size) {
+ int total_bytes_read = 0;
+ int bytes_read;
+ while (total_bytes_read < size) {
+ bytes_read = base::ReadPlatformFile(
+ file, offset + total_bytes_read, &data[total_bytes_read],
+ size - total_bytes_read);
+
+ // If we reached EOF, bytes_read will be 0.
+ if (bytes_read == 0)
+ return total_bytes_read;
+
+ if ((bytes_read < 0) || (bytes_read > size - total_bytes_read))
+ return -1;
+
+ total_bytes_read += bytes_read;
+ }
+
+ return total_bytes_read;
+}
+
+// Writes the given number of bytes to a file.
+// Returns the number of bytes written.
+int WriteFully(base::PlatformFile file, int64 offset,
+ const char* data, int size) {
+ int total_bytes_written = 0;
+ int bytes_written;
+ while (total_bytes_written < size) {
+ bytes_written = base::WritePlatformFile(
+ file, offset + total_bytes_written, &data[total_bytes_written],
+ size - total_bytes_written);
+
+ if ((bytes_written == 0) && (size == 0))
+ return 0;
+
+ if ((bytes_written <= 0) || (bytes_written > size - total_bytes_written))
+ return -1;
+
+ total_bytes_written += bytes_written;
+ }
+
+ return total_bytes_written;
+}
+
+} // namespace
+
+TEST(PlatformFile, CreatePlatformFile) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
+
+ // Open a file that doesn't exist.
+ base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
+ base::PlatformFile file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ NULL, &error_code);
+ EXPECT_EQ(base::kInvalidPlatformFileValue, file);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, error_code);
+
+ // Open or create a file.
+ bool created = false;
+ error_code = base::PLATFORM_FILE_OK;
+ file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_TRUE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ base::ClosePlatformFile(file);
+
+ // Open an existing file.
+ created = false;
+ file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_FALSE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ base::ClosePlatformFile(file);
+
+ // Create a file that exists.
+ file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_EQ(base::kInvalidPlatformFileValue, file);
+ EXPECT_FALSE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, error_code);
+
+ // Create or overwrite a file.
+ error_code = base::PLATFORM_FILE_OK;
+ file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_TRUE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ base::ClosePlatformFile(file);
+
+ // Create a delete-on-close file.
+ created = false;
+ file_path = temp_dir.path().AppendASCII("create_file_2");
+ file = base::CreatePlatformFile(
+ file_path,
+ base::PLATFORM_FILE_OPEN_ALWAYS |
+ base::PLATFORM_FILE_DELETE_ON_CLOSE |
+ base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_TRUE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+
+ EXPECT_TRUE(base::ClosePlatformFile(file));
+ EXPECT_FALSE(file_util::PathExists(file_path));
+}
+
+TEST(PlatformFile, ReadWritePlatformFile) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("read_write_file");
+ base::PlatformFile file = base::CreatePlatformFile(
+ file_path,
+ base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE,
+ NULL, NULL);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+
+ char data_to_write[] = "test";
+ const int kTestDataSize = 4;
+
+ // Write 0 bytes to the file.
+ int bytes_written = WriteFully(file, 0, data_to_write, 0);
+ EXPECT_EQ(0, bytes_written);
+
+ // Write "test" to the file.
+ bytes_written = WriteFully(file, 0, data_to_write, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ // Read from EOF.
+ char data_read_1[32];
+ int bytes_read = ReadFully(file, kTestDataSize, data_read_1, kTestDataSize);
+ EXPECT_EQ(0, bytes_read);
+
+ // Read from somewhere in the middle of the file.
+ const int kPartialReadOffset = 1;
+ bytes_read = ReadFully(file, kPartialReadOffset, data_read_1, kTestDataSize);
+ EXPECT_EQ(kTestDataSize - kPartialReadOffset, bytes_read);
+ for (int i = 0; i < bytes_read; i++)
+ EXPECT_EQ(data_to_write[i + kPartialReadOffset], data_read_1[i]);
+
+ // Read 0 bytes.
+ bytes_read = ReadFully(file, 0, data_read_1, 0);
+ EXPECT_EQ(0, bytes_read);
+
+ // Read the entire file.
+ bytes_read = ReadFully(file, 0, data_read_1, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_read);
+ for (int i = 0; i < bytes_read; i++)
+ EXPECT_EQ(data_to_write[i], data_read_1[i]);
+
+ // Write past the end of the file.
+ const int kOffsetBeyondEndOfFile = 10;
+ const int kPartialWriteLength = 2;
+ bytes_written = WriteFully(file, kOffsetBeyondEndOfFile,
+ data_to_write, kPartialWriteLength);
+ EXPECT_EQ(kPartialWriteLength, bytes_written);
+
+ // Make sure the file was extended.
+ int64 file_size = 0;
+ EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
+ EXPECT_EQ(kOffsetBeyondEndOfFile + kPartialWriteLength, file_size);
+
+ // Make sure the file was zero-padded.
+ char data_read_2[32];
+ bytes_read = ReadFully(file, 0, data_read_2, static_cast<int>(file_size));
+ EXPECT_EQ(file_size, bytes_read);
+ for (int i = 0; i < kTestDataSize; i++)
+ EXPECT_EQ(data_to_write[i], data_read_2[i]);
+ for (int i = kTestDataSize; i < kOffsetBeyondEndOfFile; i++)
+ EXPECT_EQ(0, data_read_2[i]);
+ for (int i = kOffsetBeyondEndOfFile; i < file_size; i++)
+ EXPECT_EQ(data_to_write[i - kOffsetBeyondEndOfFile], data_read_2[i]);
+
+ // Close the file handle to allow the temp directory to be deleted.
+ base::ClosePlatformFile(file);
+}
+
+TEST(PlatformFile, TruncatePlatformFile) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("truncate_file");
+ base::PlatformFile file = base::CreatePlatformFile(
+ file_path,
+ base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE,
+ NULL, NULL);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+
+ // Write "test" to the file.
+ char data_to_write[] = "test";
+ int kTestDataSize = 4;
+ int bytes_written = WriteFully(file, 0, data_to_write, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ // Extend the file.
+ const int kExtendedFileLength = 10;
+ int64 file_size = 0;
+ EXPECT_TRUE(base::TruncatePlatformFile(file, kExtendedFileLength));
+ EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
+ EXPECT_EQ(kExtendedFileLength, file_size);
+
+ // Make sure the file was zero-padded.
+ char data_read[32];
+ int bytes_read = ReadFully(file, 0, data_read, static_cast<int>(file_size));
+ EXPECT_EQ(file_size, bytes_read);
+ for (int i = 0; i < kTestDataSize; i++)
+ EXPECT_EQ(data_to_write[i], data_read[i]);
+ for (int i = kTestDataSize; i < file_size; i++)
+ EXPECT_EQ(0, data_read[i]);
+
+ // Truncate the file.
+ const int kTruncatedFileLength = 2;
+ EXPECT_TRUE(base::TruncatePlatformFile(file, kTruncatedFileLength));
+ EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
+ EXPECT_EQ(kTruncatedFileLength, file_size);
+
+ // Make sure the file was truncated.
+ bytes_read = ReadFully(file, 0, data_read, kTestDataSize);
+ EXPECT_EQ(file_size, bytes_read);
+ for (int i = 0; i < file_size; i++)
+ EXPECT_EQ(data_to_write[i], data_read[i]);
+
+ // Close the file handle to allow the temp directory to be deleted.
+ base::ClosePlatformFile(file);
+}
+
+TEST(PlatformFile, TouchGetInfoPlatformFile) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::PlatformFile file = base::CreatePlatformFile(
+ temp_dir.path().AppendASCII("touch_get_info_file"),
+ base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_WRITE_ATTRIBUTES,
+ NULL, NULL);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+
+ // Get info for a newly created file.
+ base::PlatformFileInfo info;
+ EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
+
+ // Add 2 seconds to account for possible rounding errors on
+ // filesystems that use a 1s or 2s timestamp granularity.
+ base::Time now = base::Time::Now() + base::TimeDelta::FromSeconds(2);
+ EXPECT_EQ(0, info.size);
+ EXPECT_FALSE(info.is_directory);
+ EXPECT_TRUE(info.last_accessed <= now);
+ EXPECT_TRUE(info.last_modified <= now);
+ EXPECT_TRUE(info.creation_time <= now);
+ base::Time creation_time = info.creation_time;
+
+ // Write "test" to the file.
+ char data[] = "test";
+ const int kTestDataSize = 4;
+ int bytes_written = WriteFully(file, 0, data, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ // Change the last_accessed and last_modified dates.
+ // It's best to add values that are multiples of 2 (in seconds)
+ // to the current last_accessed and last_modified times, because
+ // FATxx uses a 2s timestamp granularity.
+ base::Time new_last_accessed =
+ info.last_accessed + base::TimeDelta::FromSeconds(234);
+ base::Time new_last_modified =
+ info.last_modified + base::TimeDelta::FromMinutes(567);
+
+ EXPECT_TRUE(base::TouchPlatformFile(file, new_last_accessed,
+ new_last_modified));
+
+ // Make sure the file info was updated accordingly.
+ EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
+ EXPECT_EQ(info.size, kTestDataSize);
+ EXPECT_FALSE(info.is_directory);
+
+ // ext2/ext3 and HPS/HPS+ seem to have a timestamp granularity of 1s.
+#if defined(OS_POSIX)
+ EXPECT_EQ(info.last_accessed.ToTimeVal().tv_sec,
+ new_last_accessed.ToTimeVal().tv_sec);
+ EXPECT_EQ(info.last_modified.ToTimeVal().tv_sec,
+ new_last_modified.ToTimeVal().tv_sec);
+#else
+ EXPECT_TRUE(info.last_accessed == new_last_accessed);
+ EXPECT_TRUE(info.last_modified == new_last_modified);
+#endif
+
+ EXPECT_TRUE(info.creation_time == creation_time);
+
+ // Close the file handle to allow the temp directory to be deleted.
+ base::ClosePlatformFile(file);
+}
diff --git a/base/platform_file_win.cc b/base/platform_file_win.cc
index c4b3c08..5fd7892 100644
--- a/base/platform_file_win.cc
+++ b/base/platform_file_win.cc
@@ -61,32 +61,40 @@ PlatformFile CreatePlatformFile(const FilePath& name,
create_flags |= FILE_ATTRIBUTE_HIDDEN;
if (flags & PLATFORM_FILE_DELETE_ON_CLOSE)
create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (flags & PLATFORM_FILE_WRITE_ATTRIBUTES)
+ create_flags |= FILE_WRITE_ATTRIBUTES;
HANDLE file = CreateFile(name.value().c_str(), access, sharing, NULL,
disposition, create_flags, NULL);
- if ((flags & PLATFORM_FILE_OPEN_ALWAYS) && created &&
- INVALID_HANDLE_VALUE != file) {
- *created = (ERROR_ALREADY_EXISTS != GetLastError());
+ if (created && (INVALID_HANDLE_VALUE != file)) {
+ if (flags & PLATFORM_FILE_OPEN_ALWAYS)
+ *created = (ERROR_ALREADY_EXISTS != GetLastError());
+ else if (flags & PLATFORM_FILE_CREATE_ALWAYS)
+ *created = true;
}
- if ((file == kInvalidPlatformFileValue) && error_code) {
- DWORD last_error = GetLastError();
- switch (last_error) {
- case ERROR_SHARING_VIOLATION:
- *error_code = PLATFORM_FILE_ERROR_IN_USE;
- break;
- case ERROR_FILE_EXISTS:
- *error_code = PLATFORM_FILE_ERROR_EXISTS;
- break;
- case ERROR_FILE_NOT_FOUND:
- *error_code = PLATFORM_FILE_ERROR_NOT_FOUND;
- break;
- case ERROR_ACCESS_DENIED:
- *error_code = PLATFORM_FILE_ERROR_ACCESS_DENIED;
- break;
- default:
- *error_code = PLATFORM_FILE_ERROR_FAILED;
+ if (error_code) {
+ if (file != kInvalidPlatformFileValue)
+ *error_code = PLATFORM_FILE_OK;
+ else {
+ DWORD last_error = GetLastError();
+ switch (last_error) {
+ case ERROR_SHARING_VIOLATION:
+ *error_code = PLATFORM_FILE_ERROR_IN_USE;
+ break;
+ case ERROR_FILE_EXISTS:
+ *error_code = PLATFORM_FILE_ERROR_EXISTS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ *error_code = PLATFORM_FILE_ERROR_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ *error_code = PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ break;
+ default:
+ *error_code = PLATFORM_FILE_ERROR_FAILED;
+ }
}
}
@@ -100,7 +108,106 @@ PlatformFile CreatePlatformFile(const std::wstring& name, int flags,
}
bool ClosePlatformFile(PlatformFile file) {
- return (CloseHandle(file) == 0);
+ return (CloseHandle(file) != 0);
+}
+
+int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
+ if (file == kInvalidPlatformFileValue)
+ return -1;
+
+ LARGE_INTEGER offset_li;
+ offset_li.QuadPart = offset;
+
+ OVERLAPPED overlapped = {0};
+ overlapped.Offset = offset_li.LowPart;
+ overlapped.OffsetHigh = offset_li.HighPart;
+
+ DWORD bytes_read;
+ if (::ReadFile(file, data, size, &bytes_read, &overlapped) != 0)
+ return bytes_read;
+ else if (ERROR_HANDLE_EOF == GetLastError())
+ return 0;
+
+ return -1;
+}
+
+int WritePlatformFile(PlatformFile file, int64 offset,
+ const char* data, int size) {
+ if (file == kInvalidPlatformFileValue)
+ return -1;
+
+ LARGE_INTEGER offset_li;
+ offset_li.QuadPart = offset;
+
+ OVERLAPPED overlapped = {0};
+ overlapped.Offset = offset_li.LowPart;
+ overlapped.OffsetHigh = offset_li.HighPart;
+
+ DWORD bytes_written;
+ if (::WriteFile(file, data, size, &bytes_written, &overlapped) != 0)
+ return bytes_written;
+
+ return -1;
+}
+
+bool TruncatePlatformFile(PlatformFile file, int64 length) {
+ if (file == kInvalidPlatformFileValue)
+ return false;
+
+ // Get the current file pointer.
+ LARGE_INTEGER file_pointer;
+ LARGE_INTEGER zero;
+ zero.QuadPart = 0;
+ if (::SetFilePointerEx(file, zero, &file_pointer, FILE_CURRENT) == 0)
+ 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, 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.
+ return ((::SetEndOfFile(file) != 0) &&
+ (::SetFilePointerEx(file, file_pointer, NULL, FILE_BEGIN) != 0));
+}
+
+bool FlushPlatformFile(PlatformFile file) {
+ return ((file != kInvalidPlatformFileValue) && ::FlushFileBuffers(file));
+}
+
+bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time,
+ const base::Time& last_modified_time) {
+ if (file == kInvalidPlatformFileValue)
+ return false;
+
+ FILETIME last_access_filetime = last_access_time.ToFileTime();
+ FILETIME last_modified_filetime = last_modified_time.ToFileTime();
+ return (::SetFileTime(file, NULL, &last_access_filetime,
+ &last_modified_filetime) != 0);
+}
+
+bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
+ if (!info)
+ return false;
+
+ BY_HANDLE_FILE_INFORMATION file_info;
+ if (GetFileInformationByHandle(file, &file_info) == 0)
+ 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->last_modified = base::Time::FromFileTime(file_info.ftLastWriteTime);
+ info->last_accessed = base::Time::FromFileTime(file_info.ftLastAccessTime);
+ info->creation_time = base::Time::FromFileTime(file_info.ftCreationTime);
+ return true;
}
} // namespace disk_cache
diff --git a/base/time.h b/base/time.h
index 0ed7957..494aa57 100644
--- a/base/time.h
+++ b/base/time.h
@@ -265,7 +265,6 @@ class Time {
#if defined(OS_WIN)
static Time FromFileTime(FILETIME ft);
FILETIME ToFileTime() const;
- static Time FromLargeInteger(LARGE_INTEGER li);
// The minimum time of a low resolution timer. This is basically a windows
// constant of ~15.6ms. While it does vary on some older OS versions, we'll