0

FileAPI: Compare expected modification time with delta on file read

The File API reader validates that the "expected" modification time -
e.g. taken when a user selects a file via <input type=file> matches
the "actual" modification time when trying to read. Certain file
timestamps (on Windows/NTFS at least) are high enough precision
(tenths of microseconds) that an int64->double->int64 round trip
through Blink loses bits. Files with these timestamps can't be read!

Sample timestamp: 2011-01-01T01:23:45.999999Z (that's a lot of 9s)

Make the comparison with an epsilon of 10us.

BUG=525088
R=michaeln@chromium.org

Review URL: https://codereview.chromium.org/1308773006

Cr-Commit-Position: refs/heads/master@{#346802}
This commit is contained in:
jsbell
2015-09-01 17:55:03 -07:00
committed by Commit bot
parent a777250f56
commit 1d45da203a
2 changed files with 36 additions and 14 deletions
content/browser/fileapi
storage/browser/fileapi

@ -95,9 +95,8 @@ class LocalFileStreamReaderTest : public testing::Test {
expected_modification_time);
}
void TouchTestFile() {
base::Time new_modified_time =
test_file_modification_time() - base::TimeDelta::FromSeconds(1);
void TouchTestFile(base::TimeDelta delta) {
base::Time new_modified_time = test_file_modification_time() + delta;
ASSERT_TRUE(base::TouchFile(test_path(),
test_file_modification_time(),
new_modified_time));
@ -171,7 +170,7 @@ TEST_F(LocalFileStreamReaderTest, GetLengthNormal) {
TEST_F(LocalFileStreamReaderTest, GetLengthAfterModified) {
// Touch file so that the file's modification time becomes different
// from what we expect.
TouchTestFile();
TouchTestFile(base::TimeDelta::FromSeconds(-1));
scoped_ptr<LocalFileStreamReader> reader(
CreateFileReader(test_path(), 0, test_file_modification_time()));
@ -212,23 +211,41 @@ TEST_F(LocalFileStreamReaderTest, ReadNormal) {
TEST_F(LocalFileStreamReaderTest, ReadAfterModified) {
// Touch file so that the file's modification time becomes different
// from what we expect.
TouchTestFile();
// from what we expect. Note that the resolution on some filesystems
// is 1s so we can't test with deltas less than that.
TouchTestFile(base::TimeDelta::FromSeconds(-1));
scoped_ptr<LocalFileStreamReader> reader(
CreateFileReader(test_path(), 0, test_file_modification_time()));
int result = 0;
std::string data;
ReadFromReader(reader.get(), &data, kTestDataSize, &result);
ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
ASSERT_EQ(0U, data.size());
EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
EXPECT_EQ(0U, data.size());
// With NULL expected modification time this should work.
// Due to precision loss converting int64->double->int64 (e.g. through
// Blink) the expected/actual time may vary by microseconds. With
// modification time delta < 10us this should work.
TouchTestFile(base::TimeDelta::FromMicroseconds(1));
data.clear();
reader.reset(CreateFileReader(test_path(), 0, test_file_modification_time()));
ReadFromReader(reader.get(), &data, kTestDataSize, &result);
EXPECT_EQ(net::OK, result);
EXPECT_EQ(kTestData, data);
// With matching modification times time this should work.
TouchTestFile(base::TimeDelta());
data.clear();
reader.reset(CreateFileReader(test_path(), 0, test_file_modification_time()));
ReadFromReader(reader.get(), &data, kTestDataSize, &result);
EXPECT_EQ(net::OK, result);
EXPECT_EQ(kTestData, data);
// And with NULL expected modification time this should work.
data.clear();
reader.reset(CreateFileReader(test_path(), 0, base::Time()));
ReadFromReader(reader.get(), &data, kTestDataSize, &result);
ASSERT_EQ(net::OK, result);
ASSERT_EQ(kTestData, data);
EXPECT_EQ(net::OK, result);
EXPECT_EQ(kTestData, data);
}
TEST_F(LocalFileStreamReaderTest, ReadWithOffset) {

@ -8,13 +8,18 @@
namespace storage {
// Int64->double->int64 conversions (e.g. through Blink) may lose some
// precision in the microsecond range. Allow 10us delta.
const int kModificationTimeAllowedDeltaMicroseconds = 10;
// Verify if the underlying file has not been modified.
bool FileStreamReader::VerifySnapshotTime(
const base::Time& expected_modification_time,
const base::File::Info& file_info) {
return expected_modification_time.is_null() ||
expected_modification_time.ToTimeT() ==
file_info.last_modified.ToTimeT();
(expected_modification_time - file_info.last_modified)
.magnitude()
.InMicroseconds() < kModificationTimeAllowedDeltaMicroseconds;
}
} // namespace storage