1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ppapi/tests/test_file_io.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13
14 #include <algorithm>
15 #include <vector>
16
17 #include "ppapi/c/pp_errors.h"
18 #include "ppapi/c/ppb_file_io.h"
19 #include "ppapi/c/private/pp_file_handle.h"
20 #include "ppapi/c/private/ppb_testing_private.h"
21 #include "ppapi/cpp/file_io.h"
22 #include "ppapi/cpp/file_ref.h"
23 #include "ppapi/cpp/file_system.h"
24 #include "ppapi/cpp/instance.h"
25 #include "ppapi/cpp/module.h"
26 #include "ppapi/cpp/private/file_io_private.h"
27 #include "ppapi/cpp/private/pass_file_handle.h"
28 #include "ppapi/tests/test_utils.h"
29 #include "ppapi/tests/testing_instance.h"
30
31 #if defined(PPAPI_OS_WIN)
32 # include <io.h>
33 # include <windows.h>
34 // TODO(hamaji): Use standard windows APIs instead of compatibility layer?
35 # define lseek _lseek
36 # define read _read
37 # define write _write
38 # define ssize_t int
39 #else
40 # include <sys/mman.h>
41 # include <unistd.h>
42 #endif
43
44 REGISTER_TEST_CASE(FileIO);
45
46 namespace {
47
ReportMismatch(const std::string & method_name,const std::string & returned_result,const std::string & expected_result)48 std::string ReportMismatch(const std::string& method_name,
49 const std::string& returned_result,
50 const std::string& expected_result) {
51 return method_name + " returned '" + returned_result + "'; '" +
52 expected_result + "' expected.";
53 }
54
ReportOpenError(int32_t open_flags)55 std::string ReportOpenError(int32_t open_flags) {
56 static const char* kFlagNames[] = {
57 "PP_FILEOPENFLAG_READ",
58 "PP_FILEOPENFLAG_WRITE",
59 "PP_FILEOPENFLAG_CREATE",
60 "PP_FILEOPENFLAG_TRUNCATE",
61 "PP_FILEOPENFLAG_EXCLUSIVE"
62 };
63
64 std::string result = "FileIO:Open had unexpected behavior with flags: ";
65 bool first_flag = true;
66 for (int32_t mask = 1, index = 0; mask <= PP_FILEOPENFLAG_EXCLUSIVE;
67 mask <<= 1, ++index) {
68 if (mask & open_flags) {
69 if (first_flag) {
70 first_flag = false;
71 } else {
72 result += " | ";
73 }
74 result += kFlagNames[index];
75 }
76 }
77 if (first_flag)
78 result += "[None]";
79
80 return result;
81 }
82
ReadEntireFile(PP_Instance instance,pp::FileIO * file_io,int32_t offset,std::string * data,CallbackType callback_type)83 int32_t ReadEntireFile(PP_Instance instance,
84 pp::FileIO* file_io,
85 int32_t offset,
86 std::string* data,
87 CallbackType callback_type) {
88 TestCompletionCallback callback(instance, callback_type);
89 char buf[256];
90 int32_t read_offset = offset;
91
92 for (;;) {
93 callback.WaitForResult(
94 file_io->Read(read_offset, buf, sizeof(buf), callback.GetCallback()));
95 if (callback.result() < 0)
96 return callback.result();
97 if (callback.result() == 0)
98 break;
99 read_offset += callback.result();
100 data->append(buf, callback.result());
101 }
102
103 return PP_OK;
104 }
105
ReadToArrayEntireFile(PP_Instance instance,pp::FileIO * file_io,int32_t offset,std::string * data,CallbackType callback_type)106 int32_t ReadToArrayEntireFile(PP_Instance instance,
107 pp::FileIO* file_io,
108 int32_t offset,
109 std::string* data,
110 CallbackType callback_type) {
111 TestCompletionCallbackWithOutput< std::vector<char> > callback(
112 instance, callback_type);
113
114 for (;;) {
115 callback.WaitForResult(file_io->Read(offset, 256, callback.GetCallback()));
116 int32_t rv = callback.result();
117 if (rv < 0)
118 return rv;
119 if (rv == 0)
120 break;
121 const std::vector<char>& output = callback.output();
122 assert(rv == static_cast<int32_t>(output.size()));
123 offset += rv;
124 data->append(output.begin(), output.end());
125 }
126
127 return PP_OK;
128 }
129
ReadEntireFileFromFileHandle(int fd,std::string * data)130 bool ReadEntireFileFromFileHandle(int fd, std::string* data) {
131 if (lseek(fd, 0, SEEK_SET) < 0)
132 return false;
133 data->clear();
134
135 int ret;
136 do {
137 char buf[8192];
138 ret = read(fd, buf, sizeof(buf));
139 if (ret > 0)
140 data->append(buf, ret);
141 } while (ret > 0);
142 return ret == 0;
143 }
144
WriteEntireBuffer(PP_Instance instance,pp::FileIO * file_io,int32_t offset,const std::string & data,CallbackType callback_type)145 int32_t WriteEntireBuffer(PP_Instance instance,
146 pp::FileIO* file_io,
147 int32_t offset,
148 const std::string& data,
149 CallbackType callback_type) {
150 TestCompletionCallback callback(instance, callback_type);
151 int32_t write_offset = offset;
152 const char* buf = data.c_str();
153 int32_t size = data.size();
154
155 while (write_offset < offset + size) {
156 callback.WaitForResult(file_io->Write(write_offset,
157 &buf[write_offset - offset],
158 size - write_offset + offset,
159 callback.GetCallback()));
160 if (callback.result() < 0)
161 return callback.result();
162 if (callback.result() == 0)
163 return PP_ERROR_FAILED;
164 write_offset += callback.result();
165 }
166
167 return PP_OK;
168 }
169
170 } // namespace
171
Init()172 bool TestFileIO::Init() {
173 return CheckTestingInterface() && EnsureRunningOverHTTP();
174 }
175
RunTests(const std::string & filter)176 void TestFileIO::RunTests(const std::string& filter) {
177 RUN_CALLBACK_TEST(TestFileIO, Open, filter);
178 RUN_CALLBACK_TEST(TestFileIO, OpenDirectory, filter);
179 RUN_CALLBACK_TEST(TestFileIO, ReadWriteSetLength, filter);
180 RUN_CALLBACK_TEST(TestFileIO, ReadToArrayWriteSetLength, filter);
181 RUN_CALLBACK_TEST(TestFileIO, TouchQuery, filter);
182 RUN_CALLBACK_TEST(TestFileIO, AbortCalls, filter);
183 RUN_CALLBACK_TEST(TestFileIO, ParallelReads, filter);
184 RUN_CALLBACK_TEST(TestFileIO, ParallelWrites, filter);
185 RUN_CALLBACK_TEST(TestFileIO, NotAllowMixedReadWrite, filter);
186 RUN_CALLBACK_TEST(TestFileIO, RequestOSFileHandle, filter);
187 RUN_CALLBACK_TEST(TestFileIO, RequestOSFileHandleWithOpenExclusive, filter);
188 RUN_CALLBACK_TEST(TestFileIO, Mmap, filter);
189
190 // TODO(viettrungluu): add tests:
191 // - that PP_ERROR_PENDING is correctly returned
192 // - that operations respect the file open modes (flags)
193 }
194
TestOpen()195 std::string TestFileIO::TestOpen() {
196 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
197
198 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
199 pp::FileRef file_ref(file_system, "/file_open");
200
201 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
202 CHECK_CALLBACK_BEHAVIOR(callback);
203 ASSERT_EQ(PP_OK, callback.result());
204
205 std::string result;
206 result = MatchOpenExpectations(
207 &file_system,
208 PP_FILEOPENFLAG_READ,
209 DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS);
210 if (!result.empty())
211 return result;
212
213 // Test the behavior of the power set of
214 // { PP_FILEOPENFLAG_CREATE,
215 // PP_FILEOPENFLAG_TRUNCATE,
216 // PP_FILEOPENFLAG_EXCLUSIVE }.
217
218 // First of all, none of them are specified.
219 result = MatchOpenExpectations(
220 &file_system,
221 PP_FILEOPENFLAG_WRITE,
222 DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS);
223 if (!result.empty())
224 return result;
225
226 result = MatchOpenExpectations(
227 &file_system,
228 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE,
229 CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS);
230 if (!result.empty())
231 return result;
232
233 result = MatchOpenExpectations(
234 &file_system,
235 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_EXCLUSIVE,
236 DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS);
237 if (!result.empty())
238 return result;
239
240 result = MatchOpenExpectations(
241 &file_system,
242 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_TRUNCATE,
243 DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | TRUNCATE_IF_EXISTS);
244 if (!result.empty())
245 return result;
246
247 result = MatchOpenExpectations(
248 &file_system,
249 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE |
250 PP_FILEOPENFLAG_EXCLUSIVE,
251 CREATE_IF_DOESNT_EXIST | DONT_OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS);
252 if (!result.empty())
253 return result;
254
255 result = MatchOpenExpectations(
256 &file_system,
257 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE,
258 CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | TRUNCATE_IF_EXISTS);
259 if (!result.empty())
260 return result;
261
262 result = MatchOpenExpectations(
263 &file_system,
264 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_EXCLUSIVE |
265 PP_FILEOPENFLAG_TRUNCATE,
266 DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | TRUNCATE_IF_EXISTS);
267 if (!result.empty())
268 return result;
269
270 result = MatchOpenExpectations(
271 &file_system,
272 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE |
273 PP_FILEOPENFLAG_EXCLUSIVE | PP_FILEOPENFLAG_TRUNCATE,
274 CREATE_IF_DOESNT_EXIST | DONT_OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS);
275 if (!result.empty())
276 return result;
277
278 // Invalid combination: PP_FILEOPENFLAG_TRUNCATE without
279 // PP_FILEOPENFLAG_WRITE.
280 result = MatchOpenExpectations(
281 &file_system,
282 PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_TRUNCATE,
283 INVALID_FLAG_COMBINATION);
284 if (!result.empty())
285 return result;
286
287 PASS();
288 }
289
TestOpenDirectory()290 std::string TestFileIO::TestOpenDirectory() {
291 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
292
293 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
294 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
295 CHECK_CALLBACK_BEHAVIOR(callback);
296 ASSERT_EQ(PP_OK, callback.result());
297
298 // Make a directory.
299 pp::FileRef dir_ref(file_system, "/test_dir_open_directory");
300 callback.WaitForResult(dir_ref.MakeDirectory(
301 PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
302 CHECK_CALLBACK_BEHAVIOR(callback);
303 ASSERT_EQ(PP_OK, callback.result());
304
305 // Open the directory. This is expected to fail since directories cannot be
306 // opened.
307 pp::FileIO file_io(instance_);
308 callback.WaitForResult(file_io.Open(dir_ref, PP_FILEOPENFLAG_READ,
309 callback.GetCallback()));
310 CHECK_CALLBACK_BEHAVIOR(callback);
311 ASSERT_EQ(PP_ERROR_NOTAFILE, callback.result());
312
313 PASS();
314 }
315
TestReadWriteSetLength()316 std::string TestFileIO::TestReadWriteSetLength() {
317 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
318
319 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
320 pp::FileRef file_ref(file_system, "/file_read_write_setlength");
321 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
322 CHECK_CALLBACK_BEHAVIOR(callback);
323 ASSERT_EQ(PP_OK, callback.result());
324
325 pp::FileIO file_io(instance_);
326 callback.WaitForResult(file_io.Open(file_ref,
327 PP_FILEOPENFLAG_CREATE |
328 PP_FILEOPENFLAG_TRUNCATE |
329 PP_FILEOPENFLAG_READ |
330 PP_FILEOPENFLAG_WRITE,
331 callback.GetCallback()));
332 CHECK_CALLBACK_BEHAVIOR(callback);
333 ASSERT_EQ(PP_OK, callback.result());
334
335 // Write something to the file.
336 int32_t rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0,
337 "test_test", callback_type());
338 ASSERT_EQ(PP_OK, rv);
339
340 // Attempt to read a negative number of bytes; it should fail.
341 char buf[256];
342 callback.WaitForResult(file_io.Read(0,
343 buf,
344 -1,
345 callback.GetCallback()));
346 CHECK_CALLBACK_BEHAVIOR(callback);
347 ASSERT_EQ(PP_ERROR_FAILED, callback.result());
348
349 // Read the entire file.
350 std::string read_buffer;
351 rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer,
352 callback_type());
353 ASSERT_EQ(PP_OK, rv);
354 ASSERT_EQ(std::string("test_test"), read_buffer);
355
356 // Truncate the file.
357 callback.WaitForResult(file_io.SetLength(4, callback.GetCallback()));
358 CHECK_CALLBACK_BEHAVIOR(callback);
359 ASSERT_EQ(PP_OK, callback.result());
360
361 // Check the file contents.
362 read_buffer.clear();
363 rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer,
364 callback_type());
365 ASSERT_EQ(PP_OK, rv);
366 ASSERT_EQ(std::string("test"), read_buffer);
367
368 // Try to read past the end of the file.
369 read_buffer.clear();
370 rv = ReadEntireFile(instance_->pp_instance(), &file_io, 100, &read_buffer,
371 callback_type());
372 ASSERT_EQ(PP_OK, rv);
373 ASSERT_TRUE(read_buffer.empty());
374
375 // Write past the end of the file. The file should be zero-padded.
376 rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 8, "test",
377 callback_type());
378 ASSERT_EQ(PP_OK, rv);
379
380 // Check the contents of the file.
381 read_buffer.clear();
382 rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer,
383 callback_type());
384 ASSERT_EQ(PP_OK, rv);
385 ASSERT_EQ(std::string("test\0\0\0\0test", 12), read_buffer);
386
387 // Extend the file.
388 callback.WaitForResult(file_io.SetLength(16, callback.GetCallback()));
389 CHECK_CALLBACK_BEHAVIOR(callback);
390 ASSERT_EQ(PP_OK, callback.result());
391
392 // Check the contents of the file.
393 read_buffer.clear();
394 rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer,
395 callback_type());
396 ASSERT_EQ(PP_OK, rv);
397 ASSERT_EQ(std::string("test\0\0\0\0test\0\0\0\0", 16), read_buffer);
398
399 // Write in the middle of the file.
400 rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 4, "test",
401 callback_type());
402 ASSERT_EQ(PP_OK, rv);
403
404 // Check the contents of the file.
405 read_buffer.clear();
406 rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer,
407 callback_type());
408 ASSERT_EQ(PP_OK, rv);
409 ASSERT_EQ(std::string("testtesttest\0\0\0\0", 16), read_buffer);
410
411 // Read from the middle of the file.
412 read_buffer.clear();
413 rv = ReadEntireFile(instance_->pp_instance(), &file_io, 4, &read_buffer,
414 callback_type());
415 ASSERT_EQ(PP_OK, rv);
416 ASSERT_EQ(std::string("testtest\0\0\0\0", 12), read_buffer);
417
418 // Append to the end of the file.
419 pp::FileIO file_io2(instance_);
420 callback.WaitForResult(file_io2.Open(file_ref,
421 PP_FILEOPENFLAG_CREATE |
422 PP_FILEOPENFLAG_READ |
423 PP_FILEOPENFLAG_APPEND,
424 callback.GetCallback()));
425 rv = WriteEntireBuffer(instance_->pp_instance(), &file_io2, 0, "appended",
426 callback_type());
427 ASSERT_EQ(PP_OK, rv);
428 read_buffer.clear();
429 rv = ReadEntireFile(instance_->pp_instance(), &file_io2, 0, &read_buffer,
430 callback_type());
431 ASSERT_EQ(PP_OK, rv);
432 ASSERT_EQ(std::string("testtesttest\0\0\0\0appended", 24), read_buffer);
433
434 PASS();
435 }
436
437 // This is basically a copy of TestReadWriteSetLength, but with the new Read
438 // API. With this test case, we can make sure the two Read's have the same
439 // behavior.
TestReadToArrayWriteSetLength()440 std::string TestFileIO::TestReadToArrayWriteSetLength() {
441 if (callback_type() == PP_BLOCKING) {
442 // This test does not make sense for blocking callbacks.
443 PASS();
444 }
445 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
446
447 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
448 pp::FileRef file_ref(file_system, "/file_read_write_setlength");
449 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
450 CHECK_CALLBACK_BEHAVIOR(callback);
451 ASSERT_EQ(PP_OK, callback.result());
452
453 pp::FileIO file_io(instance_);
454 callback.WaitForResult(file_io.Open(file_ref,
455 PP_FILEOPENFLAG_CREATE |
456 PP_FILEOPENFLAG_TRUNCATE |
457 PP_FILEOPENFLAG_READ |
458 PP_FILEOPENFLAG_WRITE,
459 callback.GetCallback()));
460 CHECK_CALLBACK_BEHAVIOR(callback);
461 ASSERT_EQ(PP_OK, callback.result());
462
463 // Write something to the file.
464 int32_t rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0,
465 "test_test", callback_type());
466 ASSERT_EQ(PP_OK, rv);
467
468 TestCompletionCallbackWithOutput< std::vector<char> > callback2(
469 instance_->pp_instance(), callback_type());
470 // Attempt to read a negative number of bytes; it should fail.
471 callback2.WaitForResult(file_io.Read(0, -1, callback2.GetCallback()));
472 CHECK_CALLBACK_BEHAVIOR(callback2);
473 ASSERT_EQ(PP_ERROR_FAILED, callback2.result());
474
475 // Read the entire file.
476 std::string read_buffer;
477 read_buffer.reserve(10);
478 rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0,
479 &read_buffer, callback_type());
480 ASSERT_EQ(PP_OK, rv);
481 ASSERT_EQ(std::string("test_test"), read_buffer);
482
483 // Truncate the file.
484 callback.WaitForResult(file_io.SetLength(4, callback.GetCallback()));
485 CHECK_CALLBACK_BEHAVIOR(callback);
486 ASSERT_EQ(PP_OK, rv);
487
488 // Check the file contents.
489 read_buffer.clear();
490 rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0,
491 &read_buffer, callback_type());
492 ASSERT_EQ(PP_OK, rv);
493 ASSERT_EQ(std::string("test"), read_buffer);
494
495 // Try to read past the end of the file.
496 read_buffer.clear();
497 rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 100,
498 &read_buffer, callback_type());
499 ASSERT_EQ(PP_OK, rv);
500 ASSERT_TRUE(read_buffer.empty());
501
502 // Write past the end of the file. The file should be zero-padded.
503 rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 8, "test",
504 callback_type());
505 ASSERT_EQ(PP_OK, rv);
506
507 // Check the contents of the file.
508 read_buffer.clear();
509 rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0,
510 &read_buffer, callback_type());
511 ASSERT_EQ(PP_OK, rv);
512 ASSERT_EQ(std::string("test\0\0\0\0test", 12), read_buffer);
513
514 // Extend the file.
515 callback.WaitForResult(file_io.SetLength(16, callback.GetCallback()));
516 CHECK_CALLBACK_BEHAVIOR(callback);
517 ASSERT_EQ(PP_OK, callback.result());
518
519 // Check the contents of the file.
520 read_buffer.clear();
521 rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0,
522 &read_buffer, callback_type());
523 ASSERT_EQ(PP_OK, rv);
524 ASSERT_EQ(std::string("test\0\0\0\0test\0\0\0\0", 16), read_buffer);
525
526 // Write in the middle of the file.
527 rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 4, "test",
528 callback_type());
529 ASSERT_EQ(PP_OK, rv);
530
531 // Check the contents of the file.
532 read_buffer.clear();
533 rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0,
534 &read_buffer, callback_type());
535 ASSERT_EQ(PP_OK, rv);
536 ASSERT_EQ(std::string("testtesttest\0\0\0\0", 16), read_buffer);
537
538 // Read from the middle of the file.
539 read_buffer.clear();
540 rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 4,
541 &read_buffer, callback_type());
542 ASSERT_EQ(PP_OK, rv);
543 ASSERT_EQ(std::string("testtest\0\0\0\0", 12), read_buffer);
544
545 PASS();
546 }
547
TestTouchQuery()548 std::string TestFileIO::TestTouchQuery() {
549 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
550
551 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
552 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
553 CHECK_CALLBACK_BEHAVIOR(callback);
554 ASSERT_EQ(PP_OK, callback.result());
555
556 pp::FileRef file_ref(file_system, "/file_touch");
557 pp::FileIO file_io(instance_);
558 callback.WaitForResult(file_io.Open(file_ref,
559 PP_FILEOPENFLAG_CREATE |
560 PP_FILEOPENFLAG_TRUNCATE |
561 PP_FILEOPENFLAG_WRITE,
562 callback.GetCallback()));
563 CHECK_CALLBACK_BEHAVIOR(callback);
564 ASSERT_EQ(PP_OK, callback.result());
565
566 // Write some data to have a non-zero file size.
567 callback.WaitForResult(file_io.Write(0, "test", 4, callback.GetCallback()));
568 CHECK_CALLBACK_BEHAVIOR(callback);
569 ASSERT_EQ(4, callback.result());
570
571 const PP_Time last_access_time = 123 * 24 * 3600.0;
572 // last_modified_time's granularity is 2 seconds
573 // NOTE: In NaCl on Windows, NaClDescIO uses _fstat64 to retrieve file info.
574 // This function returns strange values for very small time values (near the
575 // Unix Epoch). For a value like 246.0, it returns -1. For larger values, it
576 // returns values that are exactly an hour less. The value below is handled
577 // correctly, and is only 100 days after the start of Unix time.
578 const PP_Time last_modified_time = 100 * 24 * 3600.0;
579 callback.WaitForResult(file_io.Touch(last_access_time, last_modified_time,
580 callback.GetCallback()));
581 CHECK_CALLBACK_BEHAVIOR(callback);
582 ASSERT_EQ(PP_OK, callback.result());
583
584 PP_FileInfo info;
585 callback.WaitForResult(file_io.Query(&info, callback.GetCallback()));
586 CHECK_CALLBACK_BEHAVIOR(callback);
587 ASSERT_EQ(PP_OK, callback.result());
588
589 if ((info.size != 4) ||
590 (info.type != PP_FILETYPE_REGULAR) ||
591 (info.system_type != PP_FILESYSTEMTYPE_LOCALTEMPORARY))
592 // Disabled due to DST-related failure: crbug.com/314579
593 //(info.last_access_time != last_access_time) ||
594 //(info.last_modified_time != last_modified_time))
595 return "FileIO::Query() has returned bad data.";
596
597 // Call |Query()| again, to make sure it works a second time.
598 callback.WaitForResult(file_io.Query(&info, callback.GetCallback()));
599 CHECK_CALLBACK_BEHAVIOR(callback);
600 ASSERT_EQ(PP_OK, callback.result());
601
602 PASS();
603 }
604
TestAbortCalls()605 std::string TestFileIO::TestAbortCalls() {
606 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
607
608 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
609 pp::FileRef file_ref(file_system, "/file_abort_calls");
610 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
611 CHECK_CALLBACK_BEHAVIOR(callback);
612 ASSERT_EQ(PP_OK, callback.result());
613
614 int32_t rv = PP_ERROR_FAILED;
615 // First, create a file on which to do ops.
616 {
617 pp::FileIO file_io(instance_);
618 callback.WaitForResult(
619 file_io.Open(file_ref,
620 PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_WRITE,
621 callback.GetCallback()));
622 CHECK_CALLBACK_BEHAVIOR(callback);
623 ASSERT_EQ(PP_OK, callback.result());
624
625 // N.B.: Should write at least 3 bytes.
626 rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0,
627 "foobarbazquux", callback_type());
628 ASSERT_EQ(PP_OK, rv);
629 }
630
631 // Abort |Open()|.
632 {
633 rv = pp::FileIO(instance_)
634 .Open(file_ref, PP_FILEOPENFLAG_READ, callback.GetCallback());
635 }
636 callback.WaitForAbortResult(rv);
637 CHECK_CALLBACK_BEHAVIOR(callback);
638
639 // Abort |Query()|.
640 {
641 PP_FileInfo info = { 0 };
642 // Save a copy and make sure |info| doesn't get written to if it is aborted.
643 PP_FileInfo info_copy;
644 memcpy(&info_copy, &info, sizeof(info));
645 {
646 pp::FileIO file_io(instance_);
647 callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_READ,
648 callback.GetCallback()));
649 CHECK_CALLBACK_BEHAVIOR(callback);
650 ASSERT_EQ(PP_OK, callback.result());
651
652 rv = file_io.Query(&info, callback.GetCallback());
653 } // Destroy |file_io|.
654 callback.WaitForResult(rv);
655 CHECK_CALLBACK_BEHAVIOR(callback);
656 if (callback_type() == PP_BLOCKING) {
657 ASSERT_EQ(PP_OK, callback.result());
658 // The operation completed synchronously, so |info| should have changed.
659 ASSERT_NE(0, memcmp(&info_copy, &info, sizeof(info)));
660 } else {
661 ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
662 ASSERT_EQ(0, memcmp(&info_copy, &info, sizeof(info)));
663 }
664 }
665
666 // Abort |Touch()|.
667 {
668 {
669 pp::FileIO file_io(instance_);
670 callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_WRITE,
671 callback.GetCallback()));
672 CHECK_CALLBACK_BEHAVIOR(callback);
673 ASSERT_EQ(PP_OK, callback.result());
674
675 rv = file_io.Touch(0, 0, callback.GetCallback());
676 } // Destroy |file_io|.
677 callback.WaitForAbortResult(rv);
678 CHECK_CALLBACK_BEHAVIOR(callback);
679 }
680
681 // Abort |Read()|.
682 {
683 char buf[3] = { 0 };
684 {
685 pp::FileIO file_io(instance_);
686 callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_READ,
687 callback.GetCallback()));
688 CHECK_CALLBACK_BEHAVIOR(callback);
689 ASSERT_EQ(PP_OK, callback.result());
690
691 rv = file_io.Read(0, buf, sizeof(buf), callback.GetCallback());
692 } // Destroy |file_io|.
693 // Save a copy to make sure buf isn't written to in the async case.
694 char buf_copy[3];
695 memcpy(&buf_copy, &buf, sizeof(buf));
696 callback.WaitForResult(rv);
697 CHECK_CALLBACK_BEHAVIOR(callback);
698 if (callback_type() == PP_BLOCKING) {
699 ASSERT_EQ(callback.result(), sizeof(buf));
700 } else {
701 ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
702 ASSERT_EQ(0, memcmp(&buf_copy, &buf, sizeof(buf)));
703 }
704 }
705
706 // Abort |Write()|.
707 {
708 char buf[3] = { 0 };
709 {
710 pp::FileIO file_io(instance_);
711 callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_WRITE,
712 callback.GetCallback()));
713 CHECK_CALLBACK_BEHAVIOR(callback);
714 ASSERT_EQ(PP_OK, callback.result());
715
716 rv = file_io.Write(0, buf, sizeof(buf), callback.GetCallback());
717 } // Destroy |file_io|.
718 callback.WaitForResult(rv);
719 CHECK_CALLBACK_BEHAVIOR(callback);
720 if (callback_type() == PP_BLOCKING)
721 ASSERT_EQ(callback.result(), sizeof(buf));
722 else
723 ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
724 }
725
726 // Abort |SetLength()|.
727 {
728 {
729 pp::FileIO file_io(instance_);
730 callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_WRITE,
731 callback.GetCallback()));
732 CHECK_CALLBACK_BEHAVIOR(callback);
733 ASSERT_EQ(PP_OK, callback.result());
734
735 rv = file_io.SetLength(3, callback.GetCallback());
736 } // Destroy |file_io|.
737 callback.WaitForAbortResult(rv);
738 CHECK_CALLBACK_BEHAVIOR(callback);
739 }
740
741 // Abort |Flush|.
742 {
743 {
744 pp::FileIO file_io(instance_);
745 callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_WRITE,
746 callback.GetCallback()));
747 CHECK_CALLBACK_BEHAVIOR(callback);
748 ASSERT_EQ(PP_OK, callback.result());
749
750 rv = file_io.Flush(callback.GetCallback());
751 } // Destroy |file_io|.
752 callback.WaitForAbortResult(rv);
753 CHECK_CALLBACK_BEHAVIOR(callback);
754 }
755
756 PASS();
757 }
758
TestParallelReads()759 std::string TestFileIO::TestParallelReads() {
760 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
761 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
762 pp::FileRef file_ref(file_system, "/file_parallel_reads");
763 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
764 CHECK_CALLBACK_BEHAVIOR(callback);
765 ASSERT_EQ(PP_OK, callback.result());
766
767 pp::FileIO file_io(instance_);
768 callback.WaitForResult(file_io.Open(file_ref,
769 PP_FILEOPENFLAG_CREATE |
770 PP_FILEOPENFLAG_TRUNCATE |
771 PP_FILEOPENFLAG_READ |
772 PP_FILEOPENFLAG_WRITE,
773 callback.GetCallback()));
774 CHECK_CALLBACK_BEHAVIOR(callback);
775 ASSERT_EQ(PP_OK, callback.result());
776
777 // Set up testing contents.
778 int32_t rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0,
779 "abcdefghijkl", callback_type());
780 ASSERT_EQ(PP_OK, rv);
781
782 // Parallel read operations.
783 const char* border = "__border__";
784 const int32_t border_size = strlen(border);
785
786 TestCompletionCallback callback_1(instance_->pp_instance(), callback_type());
787 int32_t read_offset_1 = 0;
788 int32_t size_1 = 3;
789 std::vector<char> extended_buf_1(border_size * 2 + size_1);
790 char* buf_1 = &extended_buf_1[border_size];
791 memcpy(&extended_buf_1[0], border, border_size);
792 memcpy(buf_1 + size_1, border, border_size);
793
794 TestCompletionCallback callback_2(instance_->pp_instance(), callback_type());
795 int32_t read_offset_2 = size_1;
796 int32_t size_2 = 9;
797 std::vector<char> extended_buf_2(border_size * 2 + size_2);
798 char* buf_2 = &extended_buf_2[border_size];
799 memcpy(&extended_buf_2[0], border, border_size);
800 memcpy(buf_2 + size_2, border, border_size);
801
802 int32_t rv_1 = PP_OK;
803 int32_t rv_2 = PP_OK;
804 while (size_1 >= 0 && size_2 >= 0 && size_1 + size_2 > 0) {
805 if (size_1 > 0) {
806 rv_1 = file_io.Read(read_offset_1, buf_1, size_1,
807 callback_1.GetCallback());
808 }
809 if (size_2 > 0) {
810 rv_2 = file_io.Read(read_offset_2, buf_2, size_2,
811 callback_2.GetCallback());
812 }
813 if (size_1 > 0) {
814 callback_1.WaitForResult(rv_1);
815 CHECK_CALLBACK_BEHAVIOR(callback_1);
816 ASSERT_TRUE(callback_1.result() > 0);
817 read_offset_1 += callback_1.result();
818 buf_1 += callback_1.result();
819 size_1 -= callback_1.result();
820 }
821
822 if (size_2 > 0) {
823 callback_2.WaitForResult(rv_2);
824 CHECK_CALLBACK_BEHAVIOR(callback_2);
825 ASSERT_TRUE(callback_2.result() > 0);
826 read_offset_2 += callback_2.result();
827 buf_2 += callback_2.result();
828 size_2 -= callback_2.result();
829 }
830 }
831
832 // If |size_1| or |size_2| is not 0, we have invoked wrong callback(s).
833 ASSERT_EQ(0, size_1);
834 ASSERT_EQ(0, size_2);
835
836 // Make sure every read operation writes into the correct buffer.
837 const char expected_result_1[] = "__border__abc__border__";
838 const char expected_result_2[] = "__border__defghijkl__border__";
839 ASSERT_TRUE(strncmp(&extended_buf_1[0], expected_result_1,
840 strlen(expected_result_1)) == 0);
841 ASSERT_TRUE(strncmp(&extended_buf_2[0], expected_result_2,
842 strlen(expected_result_2)) == 0);
843 PASS();
844 }
845
TestParallelWrites()846 std::string TestFileIO::TestParallelWrites() {
847 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
848 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
849 pp::FileRef file_ref(file_system, "/file_parallel_writes");
850 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
851 CHECK_CALLBACK_BEHAVIOR(callback);
852 ASSERT_EQ(PP_OK, callback.result());
853
854 pp::FileIO file_io(instance_);
855 callback.WaitForResult(file_io.Open(file_ref,
856 PP_FILEOPENFLAG_CREATE |
857 PP_FILEOPENFLAG_TRUNCATE |
858 PP_FILEOPENFLAG_READ |
859 PP_FILEOPENFLAG_WRITE,
860 callback.GetCallback()));
861 CHECK_CALLBACK_BEHAVIOR(callback);
862 ASSERT_EQ(PP_OK, callback.result());
863
864 // Parallel write operations.
865 TestCompletionCallback callback_1(instance_->pp_instance(), callback_type());
866 int32_t write_offset_1 = 0;
867 const char* buf_1 = "abc";
868 int32_t size_1 = strlen(buf_1);
869
870 TestCompletionCallback callback_2(instance_->pp_instance(), callback_type());
871 int32_t write_offset_2 = size_1;
872 const char* buf_2 = "defghijkl";
873 int32_t size_2 = strlen(buf_2);
874
875 int32_t rv_1 = PP_OK;
876 int32_t rv_2 = PP_OK;
877 while (size_1 >= 0 && size_2 >= 0 && size_1 + size_2 > 0) {
878 if (size_1 > 0) {
879 // Copy the buffer so we can erase it below.
880 std::string str_1(buf_1);
881 rv_1 = file_io.Write(
882 write_offset_1, &str_1[0], str_1.size(), callback_1.GetCallback());
883 // Erase the buffer to test that async writes copy it.
884 std::fill(str_1.begin(), str_1.end(), 0);
885 }
886 if (size_2 > 0) {
887 // Copy the buffer so we can erase it below.
888 std::string str_2(buf_2);
889 rv_2 = file_io.Write(
890 write_offset_2, &str_2[0], str_2.size(), callback_2.GetCallback());
891 // Erase the buffer to test that async writes copy it.
892 std::fill(str_2.begin(), str_2.end(), 0);
893 }
894
895 if (size_1 > 0) {
896 callback_1.WaitForResult(rv_1);
897 CHECK_CALLBACK_BEHAVIOR(callback_1);
898 ASSERT_TRUE(callback_1.result() > 0);
899 write_offset_1 += callback_1.result();
900 buf_1 += callback_1.result();
901 size_1 -= callback_1.result();
902 }
903
904 if (size_2 > 0) {
905 callback_2.WaitForResult(rv_2);
906 CHECK_CALLBACK_BEHAVIOR(callback_2);
907 ASSERT_TRUE(callback_2.result() > 0);
908 write_offset_2 += callback_2.result();
909 buf_2 += callback_2.result();
910 size_2 -= callback_2.result();
911 }
912 }
913
914 // If |size_1| or |size_2| is not 0, we have invoked wrong callback(s).
915 ASSERT_EQ(0, size_1);
916 ASSERT_EQ(0, size_2);
917
918 // Check the file contents.
919 std::string read_buffer;
920 int32_t rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0,
921 &read_buffer, callback_type());
922 ASSERT_EQ(PP_OK, rv);
923 ASSERT_EQ(std::string("abcdefghijkl"), read_buffer);
924
925 PASS();
926 }
927
TestNotAllowMixedReadWrite()928 std::string TestFileIO::TestNotAllowMixedReadWrite() {
929 if (callback_type() == PP_BLOCKING) {
930 // This test does not make sense for blocking callbacks.
931 PASS();
932 }
933 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
934
935 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
936 pp::FileRef file_ref(file_system, "/file_not_allow_mixed_read_write");
937 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
938 CHECK_CALLBACK_BEHAVIOR(callback);
939 ASSERT_EQ(PP_OK, callback.result());
940
941 pp::FileIO file_io(instance_);
942 callback.WaitForResult(file_io.Open(file_ref,
943 PP_FILEOPENFLAG_CREATE |
944 PP_FILEOPENFLAG_TRUNCATE |
945 PP_FILEOPENFLAG_READ |
946 PP_FILEOPENFLAG_WRITE,
947 callback.GetCallback()));
948 CHECK_CALLBACK_BEHAVIOR(callback);
949 ASSERT_EQ(PP_OK, callback.result());
950
951 TestCompletionCallback callback_1(instance_->pp_instance(), PP_REQUIRED);
952 int32_t write_offset_1 = 0;
953 const char* buf_1 = "mnopqrstuvw";
954 int32_t rv_1 = file_io.Write(write_offset_1, buf_1, strlen(buf_1),
955 callback_1.GetCallback());
956 ASSERT_EQ(PP_OK_COMPLETIONPENDING, rv_1);
957
958 TestCompletionCallback callback_2(instance_->pp_instance(), callback_type());
959 int32_t read_offset_2 = 4;
960 char buf_2[3];
961 callback_2.WaitForResult(file_io.Read(read_offset_2, buf_2, sizeof(buf_2),
962 callback_2.GetCallback()));
963 CHECK_CALLBACK_BEHAVIOR(callback_2);
964 ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result());
965 callback_1.WaitForResult(rv_1);
966 CHECK_CALLBACK_BEHAVIOR(callback_1);
967
968 // Cannot query while a write is pending.
969 rv_1 = file_io.Write(write_offset_1, buf_1, strlen(buf_1),
970 callback_1.GetCallback());
971 ASSERT_EQ(PP_OK_COMPLETIONPENDING, rv_1);
972 PP_FileInfo info;
973 callback_2.WaitForResult(file_io.Query(&info, callback_2.GetCallback()));
974 CHECK_CALLBACK_BEHAVIOR(callback_2);
975 ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result());
976 callback_1.WaitForResult(rv_1);
977 CHECK_CALLBACK_BEHAVIOR(callback_1);
978
979 // Cannot touch while a write is pending.
980 rv_1 = file_io.Write(write_offset_1, buf_1, strlen(buf_1),
981 callback_1.GetCallback());
982 ASSERT_EQ(PP_OK_COMPLETIONPENDING, rv_1);
983 callback_2.WaitForResult(file_io.Touch(1234.0, 5678.0,
984 callback_2.GetCallback()));
985 CHECK_CALLBACK_BEHAVIOR(callback_2);
986 ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result());
987 callback_1.WaitForResult(rv_1);
988 CHECK_CALLBACK_BEHAVIOR(callback_1);
989
990 // Cannot set length while a write is pending.
991 rv_1 = file_io.Write(write_offset_1, buf_1, strlen(buf_1),
992 callback_1.GetCallback());
993 ASSERT_EQ(PP_OK_COMPLETIONPENDING, rv_1);
994 callback_2.WaitForResult(file_io.SetLength(123, callback_2.GetCallback()));
995 CHECK_CALLBACK_BEHAVIOR(callback_2);
996 ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result());
997 callback_1.WaitForResult(rv_1);
998 CHECK_CALLBACK_BEHAVIOR(callback_1);
999
1000 PASS();
1001 }
1002
TestRequestOSFileHandle()1003 std::string TestFileIO::TestRequestOSFileHandle() {
1004 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
1005
1006 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
1007 pp::FileRef file_ref(file_system, "/file_os_fd");
1008
1009 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
1010 ASSERT_EQ(PP_OK, callback.result());
1011
1012 pp::FileIO_Private file_io(instance_);
1013 callback.WaitForResult(file_io.Open(file_ref,
1014 PP_FILEOPENFLAG_CREATE |
1015 PP_FILEOPENFLAG_TRUNCATE |
1016 PP_FILEOPENFLAG_READ |
1017 PP_FILEOPENFLAG_WRITE,
1018 callback.GetCallback()));
1019 ASSERT_EQ(PP_OK, callback.result());
1020
1021 TestCompletionCallbackWithOutput<pp::PassFileHandle> output_callback(
1022 instance_->pp_instance(), callback_type());
1023 output_callback.WaitForResult(
1024 file_io.RequestOSFileHandle(output_callback.GetCallback()));
1025 PP_FileHandle handle = output_callback.output().Release();
1026 ASSERT_EQ(PP_OK, output_callback.result());
1027
1028 if (handle == PP_kInvalidFileHandle)
1029 return "FileIO::RequestOSFileHandle() returned a bad file handle.";
1030 #if defined(PPAPI_OS_WIN)
1031 int fd = _open_osfhandle(reinterpret_cast<intptr_t>(handle),
1032 _O_RDWR | _O_BINARY);
1033 #else
1034 int fd = handle;
1035 #endif
1036 if (fd < 0)
1037 return "FileIO::RequestOSFileHandle() returned a bad file descriptor.";
1038
1039 // Check write(2) for the native FD.
1040 const std::string msg = "foobar";
1041 ssize_t cnt = write(fd, msg.data(), msg.size());
1042 if (cnt < 0)
1043 return ReportError("write for native FD returned error", errno);
1044 if (cnt != static_cast<ssize_t>(msg.size()))
1045 return ReportError("write for native FD count mismatch", cnt);
1046
1047 // Check lseek(2) for the native FD.
1048 off_t off = lseek(fd, 0, SEEK_CUR);
1049 if (off == static_cast<off_t>(-1))
1050 return ReportError("lseek for native FD returned error", errno);
1051 if (off != static_cast<off_t>(msg.size()))
1052 return ReportError("lseek for native FD offset mismatch", off);
1053
1054 off = lseek(fd, 0, SEEK_SET);
1055 if (off == static_cast<off_t>(-1))
1056 return ReportError("lseek for native FD returned error", errno);
1057 if (off != 0)
1058 return ReportError("lseek for native FD offset mismatch", off);
1059
1060 // Check read(2) for the native FD.
1061 std::string buf(msg.size(), '\0');
1062 cnt = read(fd, &buf[0], msg.size());
1063 if (cnt < 0)
1064 return ReportError("read for native FD returned error", errno);
1065 if (cnt != static_cast<ssize_t>(msg.size()))
1066 return ReportError("read for native FD count mismatch", cnt);
1067 if (msg != buf)
1068 return ReportMismatch("read for native FD", buf, msg);
1069 PASS();
1070 }
1071
1072 // Calling RequestOSFileHandle with the FileIO that is opened with
1073 // PP_FILEOPENFLAG_EXCLUSIVE used to cause NaCl module to crash while loading.
1074 // This is a regression test for crbug.com/243241.
TestRequestOSFileHandleWithOpenExclusive()1075 std::string TestFileIO::TestRequestOSFileHandleWithOpenExclusive() {
1076 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
1077
1078 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
1079 pp::FileRef file_ref(file_system, "/file_os_fd2");
1080
1081 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
1082 ASSERT_EQ(PP_OK, callback.result());
1083
1084 // Open with PP_FILEOPENFLAG_CREATE and PP_FILEOPENFLAG_EXCLUSIVE will fail
1085 // if the file already exists. Delete it here to make sure it does not.
1086 callback.WaitForResult(file_ref.Delete(callback.GetCallback()));
1087
1088 pp::FileIO_Private file_io(instance_);
1089 callback.WaitForResult(file_io.Open(file_ref,
1090 PP_FILEOPENFLAG_CREATE |
1091 PP_FILEOPENFLAG_READ |
1092 PP_FILEOPENFLAG_WRITE |
1093 PP_FILEOPENFLAG_EXCLUSIVE,
1094 callback.GetCallback()));
1095 ASSERT_EQ(PP_OK, callback.result());
1096
1097 TestCompletionCallbackWithOutput<pp::PassFileHandle> output_callback(
1098 instance_->pp_instance(), callback_type());
1099 output_callback.WaitForResult(
1100 file_io.RequestOSFileHandle(output_callback.GetCallback()));
1101 PP_FileHandle handle = output_callback.output().Release();
1102 if (handle == PP_kInvalidFileHandle)
1103 return "FileIO::RequestOSFileHandle() returned a bad file handle.";
1104 ASSERT_EQ(PP_OK, output_callback.result());
1105
1106 PASS();
1107 }
1108
TestMmap()1109 std::string TestFileIO::TestMmap() {
1110 #if !defined(PPAPI_OS_WIN)
1111 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
1112
1113 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
1114 pp::FileRef file_ref(file_system, "/file_os_fd");
1115
1116 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
1117 ASSERT_EQ(PP_OK, callback.result());
1118
1119 pp::FileIO_Private file_io(instance_);
1120 callback.WaitForResult(file_io.Open(file_ref,
1121 PP_FILEOPENFLAG_CREATE |
1122 PP_FILEOPENFLAG_TRUNCATE |
1123 PP_FILEOPENFLAG_READ |
1124 PP_FILEOPENFLAG_WRITE,
1125 callback.GetCallback()));
1126 ASSERT_EQ(PP_OK, callback.result());
1127
1128 TestCompletionCallbackWithOutput<pp::PassFileHandle> output_callback(
1129 instance_->pp_instance(), callback_type());
1130 output_callback.WaitForResult(
1131 file_io.RequestOSFileHandle(output_callback.GetCallback()));
1132 PP_FileHandle handle = output_callback.output().Release();
1133 ASSERT_EQ(PP_OK, output_callback.result());
1134
1135 if (handle == PP_kInvalidFileHandle)
1136 return "FileIO::RequestOSFileHandle() returned a bad file handle.";
1137 int fd = handle;
1138 if (fd < 0)
1139 return "FileIO::RequestOSFileHandle() returned a bad file descriptor.";
1140
1141 // Check write(2) for the native FD.
1142 const std::string msg = "foobar";
1143 ssize_t cnt = write(fd, msg.data(), msg.size());
1144 if (cnt < 0)
1145 return ReportError("write for native FD returned error", errno);
1146 if (cnt != static_cast<ssize_t>(msg.size()))
1147 return ReportError("write for native FD count mismatch", cnt);
1148
1149 // BEGIN mmap(2) test with a file handle opened in READ-WRITE mode.
1150 // Check mmap(2) for read.
1151 {
1152 char* mapped = reinterpret_cast<char*>(
1153 mmap(NULL, msg.size(), PROT_READ, MAP_PRIVATE, fd, 0));
1154 if (mapped == MAP_FAILED)
1155 return ReportError("mmap(r) for native FD returned errno", errno);
1156 // Make sure the buffer is cleared.
1157 std::string buf = std::string(msg.size(), '\0');
1158 memcpy(&buf[0], mapped, msg.size());
1159 if (msg != buf)
1160 return ReportMismatch("mmap(r) for native FD", buf, msg);
1161 int r = munmap(mapped, msg.size());
1162 if (r < 0)
1163 return ReportError("munmap for native FD returned error", errno);
1164 }
1165
1166 // Check mmap(2) for write with MAP_PRIVATE
1167 {
1168 char* mapped = reinterpret_cast<char*>(
1169 mmap(NULL, msg.size(), PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0));
1170 if (mapped == MAP_FAILED)
1171 return ReportError("mmap(r) for native FD returned errno", errno);
1172 // Make sure the file is not polluted by writing to privage mmap.
1173 strncpy(mapped, "baz", 3);
1174 std::string read_buffer;
1175 ASSERT_TRUE(ReadEntireFileFromFileHandle(fd, &read_buffer));
1176 if (msg != read_buffer)
1177 return ReportMismatch("file content != msg", read_buffer, msg);
1178 int r = munmap(mapped, msg.size());
1179 if (r < 0)
1180 return ReportError("munmap for native FD returned error", errno);
1181 }
1182
1183 // Check mmap(2) for write with MAP_SHARED.
1184 {
1185 char* mapped = reinterpret_cast<char*>(
1186 mmap(NULL, msg.size(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
1187 if (mapped == MAP_FAILED)
1188 return ReportError("mmap(w) for native FD returned errno", errno);
1189 // s/foo/baz/
1190 strncpy(mapped, "baz", 3);
1191 std::string read_buffer;
1192 ASSERT_TRUE(ReadEntireFileFromFileHandle(fd, &read_buffer));
1193 if (read_buffer != "bazbar")
1194 return ReportMismatch("file content != msg", read_buffer, "bazbar");
1195 int r = munmap(mapped, msg.size());
1196 if (r < 0)
1197 return ReportError("munmap for native FD returned error", errno);
1198 }
1199 // END mmap(2) test with a file handle opened in READ-WRITE mode.
1200
1201 if (close(fd) < 0)
1202 return ReportError("close for native FD returned error", errno);
1203
1204 // BEGIN mmap(2) test with a file handle opened in READONLY mode.
1205 file_io = pp::FileIO_Private(instance_);
1206 callback.WaitForResult(file_io.Open(file_ref,
1207 PP_FILEOPENFLAG_READ,
1208 callback.GetCallback()));
1209 ASSERT_EQ(PP_OK, callback.result());
1210
1211 output_callback = TestCompletionCallbackWithOutput<pp::PassFileHandle>(
1212 instance_->pp_instance(), callback_type());
1213 output_callback.WaitForResult(
1214 file_io.RequestOSFileHandle(output_callback.GetCallback()));
1215 handle = output_callback.output().Release();
1216 ASSERT_EQ(PP_OK, output_callback.result());
1217
1218 if (handle == PP_kInvalidFileHandle)
1219 return "FileIO::RequestOSFileHandle() returned a bad file handle.";
1220 fd = handle;
1221 if (fd < 0)
1222 return "FileIO::RequestOSFileHandle() returned a bad file descriptor.";
1223
1224 const std::string msg2 = "bazbar";
1225
1226 // Check mmap(2) for read.
1227 {
1228 char* mapped = reinterpret_cast<char*>(
1229 mmap(NULL, msg2.size(), PROT_READ, MAP_PRIVATE, fd, 0));
1230 if (mapped == MAP_FAILED)
1231 return ReportError("mmap(r) for native FD returned errno", errno);
1232 // Make sure the buffer is cleared.
1233 std::string buf = std::string(msg2.size(), '\0');
1234 memcpy(&buf[0], mapped, msg2.size());
1235 if (msg2 != buf)
1236 return ReportMismatch("mmap(r) for native FD", buf, msg2);
1237 int r = munmap(mapped, msg2.size());
1238 if (r < 0)
1239 return ReportError("munmap for native FD returned error", errno);
1240 }
1241
1242 // Check mmap(2) for write with MAP_PRIVATE
1243 {
1244 char* mapped = reinterpret_cast<char*>(
1245 mmap(NULL, msg2.size(), PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0));
1246 if (mapped == MAP_FAILED)
1247 return ReportError("mmap(r) for native FD returned errno", errno);
1248 // Make sure the file is not polluted by writing to privage mmap.
1249 strncpy(mapped, "baz", 3);
1250 std::string read_buffer;
1251 ASSERT_TRUE(ReadEntireFileFromFileHandle(fd, &read_buffer));
1252 if (msg2 != read_buffer)
1253 return ReportMismatch("file content != msg2", read_buffer, msg2);
1254 int r = munmap(mapped, msg2.size());
1255 if (r < 0)
1256 return ReportError("munmap for native FD returned error", errno);
1257 }
1258
1259 // Check mmap(2) for write with MAP_SHARED.
1260 {
1261 char* mapped = reinterpret_cast<char*>(
1262 mmap(NULL, msg2.size(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
1263 if (mapped != MAP_FAILED)
1264 return ReportError("mmap(w) for native FD must fail when opened readonly",
1265 -1);
1266 }
1267 // END mmap(2) test with a file handle opened in READONLY mode.
1268
1269 if (close(fd) < 0)
1270 return ReportError("close for native FD returned error", errno);
1271 #endif // !defined(PPAPI_OS_WIN)
1272
1273 PASS();
1274 }
1275
MatchOpenExpectations(pp::FileSystem * file_system,size_t open_flags,size_t expectations)1276 std::string TestFileIO::MatchOpenExpectations(pp::FileSystem* file_system,
1277 size_t open_flags,
1278 size_t expectations) {
1279 std::string bad_argument =
1280 "TestFileIO::MatchOpenExpectations has invalid input arguments.";
1281 bool invalid_combination = !!(expectations & INVALID_FLAG_COMBINATION);
1282 if (invalid_combination) {
1283 if (expectations != INVALID_FLAG_COMBINATION)
1284 return bad_argument;
1285 } else {
1286 // Validate that one and only one of <some_expectation> and
1287 // DONT_<some_expectation> is specified.
1288 for (size_t remains = expectations, end = END_OF_OPEN_EXPECATION_PAIRS;
1289 end != 0; remains >>= 2, end >>= 2) {
1290 if (!!(remains & 1) == !!(remains & 2))
1291 return bad_argument;
1292 }
1293 }
1294 bool create_if_doesnt_exist = !!(expectations & CREATE_IF_DOESNT_EXIST);
1295 bool open_if_exists = !!(expectations & OPEN_IF_EXISTS);
1296 bool truncate_if_exists = !!(expectations & TRUNCATE_IF_EXISTS);
1297
1298 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
1299 pp::FileRef existent_file_ref(
1300 *file_system, "/match_open_expectation_existent_non_empty_file");
1301 pp::FileRef nonexistent_file_ref(
1302 *file_system, "/match_open_expectation_nonexistent_file");
1303
1304 // Setup files for test.
1305 {
1306 callback.WaitForResult(existent_file_ref.Delete(callback.GetCallback()));
1307 CHECK_CALLBACK_BEHAVIOR(callback);
1308 ASSERT_TRUE(callback.result() == PP_OK ||
1309 callback.result() == PP_ERROR_FILENOTFOUND);
1310 callback.WaitForResult(nonexistent_file_ref.Delete(callback.GetCallback()));
1311 CHECK_CALLBACK_BEHAVIOR(callback);
1312 ASSERT_TRUE(callback.result() == PP_OK ||
1313 callback.result() == PP_ERROR_FILENOTFOUND);
1314
1315 pp::FileIO existent_file_io(instance_);
1316 callback.WaitForResult(existent_file_io.Open(
1317 existent_file_ref,
1318 PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_WRITE,
1319 callback.GetCallback()));
1320 CHECK_CALLBACK_BEHAVIOR(callback);
1321 ASSERT_EQ(PP_OK, callback.result());
1322 int32_t rv = WriteEntireBuffer(instance_->pp_instance(), &existent_file_io,
1323 0, "foobar", callback_type());
1324 ASSERT_EQ(PP_OK, rv);
1325 }
1326
1327 pp::FileIO existent_file_io(instance_);
1328 callback.WaitForResult(existent_file_io.Open(existent_file_ref, open_flags,
1329 callback.GetCallback()));
1330 CHECK_CALLBACK_BEHAVIOR(callback);
1331 if ((invalid_combination && callback.result() == PP_OK) ||
1332 (!invalid_combination &&
1333 ((callback.result() == PP_OK) != open_if_exists))) {
1334 return ReportOpenError(open_flags);
1335 }
1336
1337 if (!invalid_combination && open_if_exists) {
1338 PP_FileInfo info;
1339 callback.WaitForResult(existent_file_io.Query(&info,
1340 callback.GetCallback()));
1341 CHECK_CALLBACK_BEHAVIOR(callback);
1342 ASSERT_EQ(PP_OK, callback.result());
1343 if (truncate_if_exists != (info.size == 0))
1344 return ReportOpenError(open_flags);
1345 }
1346
1347 pp::FileIO nonexistent_file_io(instance_);
1348 callback.WaitForResult(nonexistent_file_io.Open(nonexistent_file_ref,
1349 open_flags,
1350 callback.GetCallback()));
1351 CHECK_CALLBACK_BEHAVIOR(callback);
1352 if ((invalid_combination && callback.result() == PP_OK) ||
1353 (!invalid_combination &&
1354 ((callback.result() == PP_OK) != create_if_doesnt_exist))) {
1355 return ReportOpenError(open_flags);
1356 }
1357
1358 return std::string();
1359 }
1360
1361 // TODO(viettrungluu): Test Close(). crbug.com/69457
1362