Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Side by Side Diff: native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc

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

Powered by Google App Engine
This is Rietveld 408576698