OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 "nacl_io/googledrivefs/googledrivefs_node.h" | |
6 | |
7 #include <stdint.h> | |
8 #include <stdio.h> | |
9 #include <stdlib.h> | |
10 #include <string.h> | |
11 | |
12 #include <algorithm> | |
13 #include <limits> | |
14 | |
15 #include "ppapi/c/pp_completion_callback.h" | |
16 | |
17 #include "nacl_io/error.h" | |
18 #include "nacl_io/filesystem.h" | |
19 #include "nacl_io/getdents_helper.h" | |
20 #include "nacl_io/hash.h" | |
21 #include "nacl_io/kernel_handle.h" | |
22 #include "nacl_io/statuscode.h" | |
23 #include "nacl_io/googledrivefs/googledrivefs.h" | |
24 #include "nacl_io/googledrivefs/googledrivefs_util.h" | |
25 | |
26 namespace nacl_io { | |
27 | |
28 GoogleDriveFsNode::GoogleDriveFsNode(Filesystem* filesystem, Path path) | |
29 : Node(filesystem), path_(path) {} | |
30 | |
31 Error GoogleDriveFsNode::GetDents(size_t offs, | |
32 struct dirent* pdir, | |
33 size_t size, | |
34 int* out_bytes) { | |
35 *out_bytes = 0; | |
36 | |
37 if (!IsaDir()) { | |
38 return ENOTDIR; | |
39 } | |
40 | |
41 GetDentsHelper helper(HashPath(Path(".")), HashPath(Path(".."))); | |
42 | |
43 std::vector<std::string> dirent_names; | |
44 Error error = RequestDirent("", &dirent_names); | |
45 if (error) { | |
46 return error; | |
47 } | |
48 | |
49 for (size_t i = 0; i < dirent_names.size(); ++i) { | |
50 Path child_path(path_); | |
51 child_path = child_path.Append("/" + dirent_names[i]); | |
52 ino_t child_ino = HashPath(child_path); | |
53 | |
54 helper.AddDirent(child_ino, dirent_names[i].c_str(), | |
55 dirent_names[i].size()); | |
56 } | |
57 | |
58 return helper.GetDents(offs, pdir, size, out_bytes); | |
59 } | |
60 | |
61 Error GoogleDriveFsNode::GetStat(struct stat* pstat) { | |
62 Error error = GetSize(&pstat->st_size); | |
63 if (error) { | |
64 return error; | |
65 } | |
66 | |
67 error = GetModifiedTime(&pstat->st_mtime); | |
68 if (error) { | |
69 return error; | |
70 } | |
71 | |
72 pstat->st_atime = 0; | |
73 pstat->st_ctime = 0; | |
74 | |
75 pstat->st_mode = stat_.st_mode; | |
76 | |
77 return 0; | |
78 } | |
79 | |
80 Error GoogleDriveFsNode::Write(const HandleAttr& attr, | |
81 const void* buf, | |
82 size_t count, | |
83 int* out_bytes) { | |
84 *out_bytes = 0; | |
85 | |
86 if (IsaDir()) { | |
87 return EISDIR; | |
88 } | |
89 if ((GetMode() & S_IWRITE) == 0) { | |
90 return EACCES; | |
91 } | |
92 | |
93 off_t file_size; | |
94 Error error = GetSize(&file_size); | |
95 if (error) { | |
96 return error; | |
97 } | |
98 | |
99 if (attr.offs > file_size || | |
100 attr.offs >= std::numeric_limits<int32_t>::max()) { | |
101 return EFBIG; | |
102 } | |
103 | |
104 // GoogleDriveFs has a maximum file size of INT32_MAX (i.e. | |
105 // std::numeric_limits<int32_t>::max()). | |
106 int bytes_to_write = | |
107 std::min<size_t>(count, std::numeric_limits<int32_t>::max()); | |
108 bytes_to_write = std::min<int32_t>( | |
109 bytes_to_write, std::numeric_limits<int32_t>::max() - attr.offs); | |
110 | |
111 int32_t file_buffer_size = | |
112 std::max<int32_t>(file_size, attr.offs + bytes_to_write); | |
113 | |
114 // use std::string for storing data in the heap, as the size of stack | |
115 // is measured in megabytes, disallowing files larger than that. | |
116 std::string file_buffer(file_buffer_size, '\0'); | |
117 | |
118 if (file_size > 0) { | |
119 int32_t read_helper_out_bytes; | |
120 error = ReadHelper(0, file_size, &file_buffer[0], &read_helper_out_bytes); | |
121 if (error) { | |
122 return error; | |
123 } | |
124 } | |
125 | |
126 memcpy(&file_buffer[0] + attr.offs, buf, bytes_to_write); | |
127 | |
128 error = WriteHelper(file_buffer.c_str(), file_buffer_size); | |
129 if (error) { | |
130 return error; | |
131 } | |
132 | |
133 *out_bytes = bytes_to_write; | |
134 | |
135 return 0; | |
136 } | |
137 | |
138 Error GoogleDriveFsNode::FTruncate(off_t length) { | |
139 if (IsaDir()) { | |
140 return EISDIR; | |
141 } | |
142 | |
143 // GoogleDriveFs has a maximum file size of INT32_MAX (i.e. | |
144 // std::numeric_limits<int32_t>::max()). | |
145 if (length > std::numeric_limits<int32_t>::max()) { | |
146 return EFBIG; | |
147 } | |
148 | |
149 off_t file_size; | |
150 Error error = GetSize(&file_size); | |
151 if (error) { | |
152 return error; | |
153 } | |
154 | |
155 std::string file_buffer(length, '\0'); | |
156 | |
157 if (file_size > 0) { | |
158 int32_t read_helper_out_bytes; | |
159 int32_t read_end = std::min<int32_t>(length, file_size); | |
160 error = ReadHelper(0, read_end, &file_buffer[0], &read_helper_out_bytes); | |
161 if (error) { | |
162 return error; | |
163 } | |
164 } | |
165 | |
166 error = WriteHelper(file_buffer.c_str(), length); | |
167 if (error) { | |
168 return error; | |
169 } | |
170 | |
171 return 0; | |
172 } | |
173 | |
174 Error GoogleDriveFsNode::Read(const HandleAttr& attr, | |
175 void* buf, | |
176 size_t count, | |
177 int* out_bytes) { | |
178 *out_bytes = 0; | |
179 | |
180 if (IsaDir()) { | |
181 return EISDIR; | |
182 } | |
183 if ((GetMode() & S_IREAD) == 0) { | |
184 return EACCES; | |
185 } | |
186 | |
187 if (count == 0) { | |
188 return 0; | |
189 } | |
190 | |
191 // GoogleDriveFs has a maximum file size of INT32_MAX (i.e. | |
192 // std::numeric_limits<int32_t>::max()). | |
193 if (attr.offs >= std::numeric_limits<int32_t>::max()) { | |
chanpatorikku
2016/09/21 16:41:10
In patch set 2 and patch set 3, I wrote:
“Google D
| |
194 return 0; | |
195 } | |
196 | |
197 // Suppose int is >= 32 bits, bytes_to_read is adjusted to | |
198 // be in the range of a Google Drive file. | |
199 int bytes_to_read = | |
200 std::min<size_t>(count, std::numeric_limits<int32_t>::max()); | |
201 bytes_to_read = std::min<int32_t>( | |
202 bytes_to_read, std::numeric_limits<int32_t>::max() - attr.offs); | |
203 | |
204 int32_t read_helper_out_bytes; | |
205 Error error = ReadHelper(attr.offs, attr.offs + bytes_to_read, buf, | |
206 &read_helper_out_bytes); | |
207 if (error) { | |
208 return error; | |
209 } | |
210 | |
211 *out_bytes = read_helper_out_bytes; | |
212 | |
213 return 0; | |
214 } | |
215 | |
216 Error GoogleDriveFsNode::GetSize(off_t* out_size) { | |
217 *out_size = 0; | |
218 | |
219 if (IsaDir()) { | |
220 return 0; | |
221 } | |
222 | |
223 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_); | |
224 | |
225 RequestUrlParams p; | |
226 | |
227 p.url = DRIVE_URL; | |
228 AddUrlPath(item_id_, &p.url); | |
229 AddUrlFirstQueryParameter("fields", "size", &p.url); | |
230 | |
231 p.method = "GET"; | |
232 | |
233 AddHeaders("Content-type", "application/json", &p.headers); | |
234 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); | |
235 | |
236 ScopedResource url_response_info_resource(googledrivefs->ppapi()); | |
237 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource); | |
238 if (error) { | |
239 return error; | |
240 } | |
241 | |
242 if (ReadStatusCode(googledrivefs->ppapi(), | |
243 url_response_info_resource.pp_resource()) != | |
244 STATUSCODE_OK) { | |
245 return EPERM; | |
246 } | |
247 | |
248 std::string output; | |
249 error = ReadResponseBody(googledrivefs->ppapi(), | |
250 url_response_info_resource.pp_resource(), | |
251 std::numeric_limits<int32_t>::max(), &output); | |
252 if (error) { | |
253 return error; | |
254 } | |
255 | |
256 std::string size_value; | |
257 size_t size_index; | |
258 error = | |
259 GetValueStringAndValuePos(output, "size", 0, &size_value, &size_index); | |
260 if (error == EINVAL) { | |
261 size_value = "0"; | |
262 } else if (error) { | |
263 return error; | |
264 } | |
265 | |
266 // GoogleDriveFs has a maximum file size of INT32_MAX (i.e. | |
267 // std::numeric_limits<int32_t>::max()). | |
268 *out_size = atoi(size_value.c_str()); | |
269 | |
270 return 0; | |
271 } | |
272 | |
273 Error GoogleDriveFsNode::Init(int open_flags) { | |
274 Error error = Node::Init(open_flags); | |
275 if (error) { | |
276 return error; | |
277 } | |
278 | |
279 if (!filesystem_->ppapi()) { | |
280 return ENOSYS; | |
281 } | |
282 | |
283 if (path_.IsRoot()) { | |
284 item_id_ = "root"; | |
285 SetType(S_IFDIR); | |
286 SetMode(S_IREAD); | |
287 return 0; | |
288 } | |
289 | |
290 error = RequestParentDirId(path_, static_cast<GoogleDriveFs*>(filesystem_), | |
291 &parent_dir_id_); | |
292 if (error) { | |
293 return error; | |
294 } | |
295 | |
296 // Request the ID of an item, which is a file or a directory, | |
297 // and the item type. | |
298 bool is_dir_type; | |
299 error = RequestItemIdAndItemType(parent_dir_id_, path_.Basename(), | |
300 static_cast<GoogleDriveFs*>(filesystem_), | |
301 &item_id_, &is_dir_type); | |
302 | |
303 if (error == ENOENT) { | |
304 // Only files are open as mode O_CREAT | |
305 if ((open_flags & O_CREAT) != 0) { | |
306 error = CreateEmptyFile(); | |
307 if (error) { | |
308 return error; | |
309 } | |
310 error = RequestItemIdAndItemType(parent_dir_id_, path_.Basename(), | |
311 static_cast<GoogleDriveFs*>(filesystem_), | |
312 &item_id_, &is_dir_type); | |
313 if (error) { | |
314 return error; | |
315 } | |
316 SetType(S_IFREG); | |
317 if ((open_flags & O_RDWR) != 0) { | |
318 SetMode(S_IREAD | S_IWRITE); | |
319 } else if ((open_flags & O_WRONLY) != 0) { | |
320 SetMode(S_IWRITE); | |
321 } else { | |
322 SetMode(S_IREAD); | |
323 } | |
324 } else { | |
325 return ENOENT; | |
326 } | |
327 } else if (error) { | |
328 return error; | |
329 } else { | |
330 if (is_dir_type) { | |
331 SetType(S_IFDIR); | |
332 SetMode(S_IREAD); | |
333 } else { | |
334 SetType(S_IFREG); | |
335 if ((open_flags & O_RDWR) != 0) { | |
336 SetMode(S_IREAD | S_IWRITE); | |
337 } else if ((open_flags & O_WRONLY) != 0) { | |
338 SetMode(S_IWRITE); | |
339 } else { | |
340 SetMode(S_IREAD); | |
341 } | |
342 } | |
343 | |
344 if (open_flags == 0) { | |
345 // open_flags == 0 for file opened on fopen with mode r and | |
346 // directory opened on opendir | |
347 return 0; | |
348 } else if (IsaDir()) { | |
349 return EPERM; | |
350 } else if ((open_flags & O_TRUNC) != 0) { | |
351 error = WriteHelper(NULL, 0); | |
352 if (error) { | |
353 return error; | |
354 } | |
355 } | |
356 } | |
357 | |
358 return 0; | |
359 } | |
360 | |
361 Error GoogleDriveFsNode::ReadHelper(int32_t start, | |
362 int32_t end, | |
363 void* out_buffer, | |
364 int32_t* out_bytes) { | |
365 memset(out_buffer, 0, 1); | |
366 *out_bytes = 0; | |
367 | |
368 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_); | |
369 | |
370 RequestUrlParams p; | |
371 | |
372 p.url = DOWNLOAD_DRIVE_URL; | |
373 AddUrlPath(item_id_, &p.url); | |
374 AddUrlFirstQueryParameter("alt", "media", &p.url); | |
375 | |
376 p.method = "GET"; | |
377 | |
378 AddHeaders("Content-type", "application/json", &p.headers); | |
379 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); | |
380 | |
381 char range_value_buffer[1024]; | |
382 snprintf(range_value_buffer, sizeof(range_value_buffer), "bytes=%i-%i", start, | |
383 end - 1); | |
chanpatorikku
2016/09/21 16:41:10
bytes=%i-%i
can become
bytes=0--1
when start =
| |
384 | |
385 AddHeaders("Range", range_value_buffer, &p.headers); | |
386 | |
387 ScopedResource url_response_info_resource(googledrivefs->ppapi()); | |
388 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource); | |
389 if (error) { | |
390 return error; | |
391 } | |
392 | |
393 int32_t status_code = ReadStatusCode( | |
394 googledrivefs->ppapi(), url_response_info_resource.pp_resource()); | |
395 if (status_code == STATUSCODE_OK || | |
396 status_code == STATUSCODE_PARTIAL_CONTENT) { | |
397 std::string output; | |
398 error = ReadResponseBody(googledrivefs->ppapi(), | |
399 url_response_info_resource.pp_resource(), | |
400 end - start, &output); | |
401 if (error) { | |
402 return error; | |
403 } | |
404 | |
405 memcpy(out_buffer, &output[0], output.size()); | |
406 *out_bytes = output.size(); | |
407 | |
408 return 0; | |
409 } else if (status_code == STATUSCODE_REQUESTED_RANGE_NOT_SATISFIABLE) { | |
410 return 0; | |
411 } | |
412 | |
413 return EPERM; | |
414 } | |
415 | |
416 Error GoogleDriveFsNode::WriteHelper(const char* body_data, int32_t body_size) { | |
417 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_); | |
418 | |
419 RequestUrlParams p; | |
420 | |
421 p.url = UPLOAD_DRIVE_URL; | |
422 AddUrlPath(item_id_, &p.url); | |
423 AddUrlFirstQueryParameter("uploadType", "media", &p.url); | |
424 | |
425 p.method = "PATCH"; | |
426 | |
427 AddHeaders("Content-type", "application/json", &p.headers); | |
428 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); | |
429 | |
430 p.body = std::string(body_data, body_size); | |
431 | |
432 ScopedResource url_response_info_resource(googledrivefs->ppapi()); | |
433 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource); | |
434 if (error) { | |
435 return error; | |
436 } | |
437 | |
438 if (ReadStatusCode(googledrivefs->ppapi(), | |
439 url_response_info_resource.pp_resource()) != | |
440 STATUSCODE_OK) { | |
441 return EPERM; | |
442 } | |
443 | |
444 return 0; | |
445 } | |
446 | |
447 Error GoogleDriveFsNode::GetModifiedTime(time_t* out_mtime) { | |
448 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_); | |
449 | |
450 RequestUrlParams p; | |
451 | |
452 p.url = DRIVE_URL; | |
453 AddUrlPath(item_id_, &p.url); | |
454 AddUrlFirstQueryParameter("fields", "modifiedTime", &p.url); | |
455 | |
456 p.method = "GET"; | |
457 | |
458 AddHeaders("Content-type", "application/json", &p.headers); | |
459 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); | |
460 | |
461 ScopedResource url_response_info_resource(googledrivefs->ppapi()); | |
462 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource); | |
463 if (error) { | |
464 return error; | |
465 } | |
466 | |
467 if (ReadStatusCode(googledrivefs->ppapi(), | |
468 url_response_info_resource.pp_resource()) != | |
469 STATUSCODE_OK) { | |
470 return EPERM; | |
471 } | |
472 | |
473 std::string output; | |
474 error = ReadResponseBody(googledrivefs->ppapi(), | |
475 url_response_info_resource.pp_resource(), | |
476 std::numeric_limits<int32_t>::max(), &output); | |
477 if (error) { | |
478 return error; | |
479 } | |
480 | |
481 std::string modified_time_value; | |
482 size_t modified_time_index; | |
483 error = GetValueStringAndValuePos(output, "modifiedTime", 0, | |
484 &modified_time_value, &modified_time_index); | |
485 if (error) { | |
486 return EPERM; | |
487 } | |
488 | |
489 *out_mtime = | |
490 ConvertDateTimeToEpochTime(ExtractYearFromRFC3339(modified_time_value), | |
491 ExtractMonthFromRFC3339(modified_time_value), | |
492 ExtractDayFromRFC3339(modified_time_value), | |
493 ExtractHourFromRFC3339(modified_time_value), | |
494 ExtractMinuteFromRFC3339(modified_time_value), | |
495 ExtractSecondFromRFC3339(modified_time_value)); | |
496 | |
497 return 0; | |
498 } | |
499 | |
500 Error GoogleDriveFsNode::CreateEmptyFile() { | |
501 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_); | |
502 | |
503 RequestUrlParams p; | |
504 std::string BOUNDARY_VALUE = "foo_bar_baz"; | |
505 | |
506 p.url = UPLOAD_DRIVE_URL; | |
507 AddUrlFirstQueryParameter("uploadType", "multipart", &p.url); | |
508 | |
509 p.method = "POST"; | |
510 | |
511 AddHeaders("Content-type", "multipart/related; boundary=" + BOUNDARY_VALUE, | |
512 &p.headers); | |
513 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); | |
514 | |
515 AddBody("--" + BOUNDARY_VALUE, &p.body); | |
516 AddBody("Content-Type: application/json; charset=UTF-8", &p.body); | |
517 AddBody("", &p.body); | |
518 AddBody("{", &p.body); | |
519 AddBody(" \"name\": \"" + path_.Basename() + "\",", &p.body); | |
520 AddBody(" \"parents\": [", &p.body); | |
521 AddBody(" \"" + parent_dir_id_ + "\"", &p.body); | |
522 AddBody(" ]", &p.body); | |
523 AddBody("}", &p.body); | |
524 AddBody("", &p.body); | |
525 AddBody("--" + BOUNDARY_VALUE, &p.body); | |
526 AddBody("Content-Type: text/plain", &p.body); | |
527 AddBody("", &p.body); | |
528 AddBody("", &p.body); | |
529 AddBody("--" + BOUNDARY_VALUE + "--", &p.body); | |
530 | |
531 ScopedResource url_response_info_resource(googledrivefs->ppapi()); | |
532 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource); | |
533 if (error) { | |
534 return error; | |
535 } | |
536 | |
537 if (ReadStatusCode(googledrivefs->ppapi(), | |
538 url_response_info_resource.pp_resource()) != | |
539 STATUSCODE_OK) { | |
540 return EPERM; | |
541 } | |
542 | |
543 return 0; | |
544 } | |
545 | |
546 Error GoogleDriveFsNode::RequestDirent( | |
547 const std::string& optional_page_token, | |
548 std::vector<std::string>* out_dirent_names) { | |
549 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_); | |
550 | |
551 RequestUrlParams p; | |
552 | |
553 p.url = DRIVE_URL; | |
554 AddUrlFirstQueryParameter("q", ParentEqualClause(item_id_), &p.url); | |
555 | |
556 if (!optional_page_token.empty()) { | |
557 AddUrlNextQueryParameter("pageToken", optional_page_token, &p.url); | |
558 } | |
559 | |
560 p.method = "GET"; | |
561 | |
562 AddHeaders("Content-type", "application/json", &p.headers); | |
563 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); | |
564 | |
565 ScopedResource url_response_info_resource(googledrivefs->ppapi()); | |
566 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource); | |
567 if (error) { | |
568 return error; | |
569 } | |
570 | |
571 if (ReadStatusCode(googledrivefs->ppapi(), | |
572 url_response_info_resource.pp_resource()) != | |
573 STATUSCODE_OK) { | |
574 return EPERM; | |
575 } | |
576 | |
577 std::string output; | |
578 error = ReadResponseBody(googledrivefs->ppapi(), | |
579 url_response_info_resource.pp_resource(), | |
580 std::numeric_limits<int32_t>::max(), &output); | |
581 if (error) { | |
582 return error; | |
583 } | |
584 | |
585 std::string name_value; | |
586 size_t name_index; | |
587 error = | |
588 GetValueStringAndValuePos(output, "name", 0, &name_value, &name_index); | |
589 if (error && error != EINVAL) { | |
590 return error; | |
591 } | |
592 | |
593 while (!error) { | |
594 out_dirent_names->push_back(name_value); | |
595 | |
596 error = GetValueStringAndValuePos(output, "name", name_index, &name_value, | |
597 &name_index); | |
598 if (error && error != EINVAL) { | |
599 return error; | |
600 } | |
601 } | |
602 | |
603 std::string next_page_token_value; | |
604 size_t next_page_token_index; | |
605 error = | |
606 GetValueStringAndValuePos(output, "nextPageToken", 0, | |
607 &next_page_token_value, &next_page_token_index); | |
608 if (!error) { | |
609 return RequestDirent(next_page_token_value, out_dirent_names); | |
610 } else if (error != EINVAL) { | |
611 return error; | |
612 } | |
613 | |
614 return 0; | |
615 } | |
616 | |
617 } // namespace nacl_io | |
OLD | NEW |