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

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 <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 // a Google Drive file has a max size of max of int32_t by design
binji 2016/09/20 01:22:05 This comment doesn't make much sense to me. Really
chanpatorikku 2016/09/21 16:41:09 Quick fix on the comments. Less time is going to
105 // so the program logic is simpler when off_t in some platform is
106 // 32 bit and off_t in another platform is 64 bit.
107 // *out_bytes is in int. Suppose int is >= 32 bits, figure the number of
108 // necessary bytes to write so after GoogleDriveFsNode::Write(..),
109 // the Google Drive file size is <= max of int32_t.
110 int bytes_to_write =
111 std::min<size_t>(count, std::numeric_limits<int32_t>::max());
112 bytes_to_write = std::min<int32_t>(
113 bytes_to_write, std::numeric_limits<int32_t>::max() - attr.offs);
114
115 int32_t file_buffer_size =
116 std::max<int32_t>(file_size, attr.offs + bytes_to_write);
117
118 // use std::string for storing data in the heap, as the size of stack
119 // is measured in megabytes, disallowing files larger than that.
120 std::string file_buffer(file_buffer_size, '\0');
121
122 if (file_size > 0) {
123 int32_t read_helper_out_bytes;
binji 2016/09/20 01:22:04 this isn't used? Is it not possible for ReadHelper
chanpatorikku 2016/09/21 16:41:09 This isn't used here, but it is possible for ReadH
124 error =
125 ReadHelper(0, file_size - 1, &file_buffer[0], &read_helper_out_bytes);
binji 2016/09/20 01:22:04 This looks a bit strange since the end is inclusiv
binji 2016/09/20 01:22:05 what if the file size changes between the call to
chanpatorikku 2016/09/21 16:41:09 No, http mount does not do the same thing. The en
chanpatorikku 2016/09/21 16:41:09 GoogleDriveFs is not going to correctly work. The
126 if (error) {
127 return error;
128 }
129 }
130
131 memcpy(&file_buffer[0] + attr.offs, buf, bytes_to_write);
binji 2016/09/20 01:22:04 Can you add an assertion before this memcpy that:
chanpatorikku 2016/09/21 16:41:09 Yes. An assertion, however, does not have to be a
binji 2016/09/26 19:11:12 Yes, but in this case it was not as clear to me th
132
133 error = WriteHelper(file_buffer.c_str(), file_buffer_size);
134 if (error) {
135 return error;
136 }
137
138 *out_bytes = bytes_to_write;
139
140 return 0;
141 }
142
143 Error GoogleDriveFsNode::FTruncate(off_t length) {
144 if (IsaDir()) {
145 return EISDIR;
146 }
147
148 // A Google Drive file size is <= max of int32_t by design
149 // so the program logic is simpler when off_t in some platform is
150 // 32 bit and off_t in another platform is 64 bit.
151 // Make sure length <= max of int32_t so after
152 // GoogleDriveFsNode::FTruncate(..), the Google Drive file size
153 // is <= max of int32_t.
154 if (length > std::numeric_limits<int32_t>::max()) {
155 return EFBIG;
156 }
157
158 off_t file_size;
159 Error error = GetSize(&file_size);
160 if (error) {
161 return error;
162 }
163
164 std::string file_buffer(length, '\0');
165
166 if (file_size > 0) {
167 int32_t read_helper_out_bytes;
168 int32_t read_end = std::min<int32_t>(length, file_size);
169 error =
170 ReadHelper(0, read_end - 1, &file_buffer[0], &read_helper_out_bytes);
171 if (error) {
172 return error;
173 }
174 }
175
176 error = WriteHelper(file_buffer.c_str(), length);
177 if (error) {
178 return error;
179 }
180
181 return 0;
182 }
183
184 Error GoogleDriveFsNode::Read(const HandleAttr& attr,
185 void* buf,
186 size_t count,
187 int* out_bytes) {
188 *out_bytes = 0;
189
190 if (IsaDir()) {
191 return EISDIR;
192 }
193 if ((GetMode() & S_IREAD) == 0) {
194 return EACCES;
195 }
196
197 if (count == 0) {
chanpatorikku 2016/09/06 14:49:38 Return 0 when count == 0 so the later code: Err
198 return 0;
199 }
200
201 // a Google Drive file has a max size of max of int32_t by design
202 // so the program logic is simpler when off_t in some platform is
203 // 32 bit and off_t in another platform is 64 bit.
204 // Google Drive files written from nacl_io can be only up
205 // to INT32_MAX bytes.
206 if (attr.offs >= std::numeric_limits<int32_t>::max()) {
207 return 0;
208 }
209
210 // Suppose int is >= 32 bits, bytes_to_read is adjusted to
211 // be in the range of a Google Drive file.
212 int bytes_to_read =
213 std::min<size_t>(count, std::numeric_limits<int32_t>::max());
214 bytes_to_read = std::min<int32_t>(
215 bytes_to_read, std::numeric_limits<int32_t>::max() - attr.offs);
216
217 int32_t read_helper_out_bytes;
218 Error error = ReadHelper(attr.offs, attr.offs + bytes_to_read - 1, buf,
219 &read_helper_out_bytes);
220 if (error) {
221 return error;
222 }
223
224 *out_bytes = read_helper_out_bytes;
225
226 return 0;
227 }
228
229 Error GoogleDriveFsNode::GetSize(off_t* out_size) {
230 *out_size = 0;
231
232 if (IsaDir()) {
233 return 0;
234 }
235
236 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
237
238 RequestUrlParams p;
239
240 p.url = DRIVE_URL;
241 AddUrlPath(item_id_, &p.url);
242 AddUrlFirstQueryParameter("fields", "size", &p.url);
243
244 p.method = "GET";
245
246 AddHeaders("Content-type", "application/json", &p.headers);
247 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
248
249 ScopedResource url_response_info_resource(googledrivefs->ppapi());
250 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
251 if (error) {
252 return error;
253 }
254
255 if (ReadStatusCode(googledrivefs->ppapi(),
256 url_response_info_resource.pp_resource()) !=
257 STATUSCODE_OK) {
258 return EPERM;
259 }
260
261 std::string output;
262 error = ReadResponseBody(googledrivefs->ppapi(),
263 url_response_info_resource.pp_resource(),
264 std::numeric_limits<int32_t>::max(), &output);
265 if (error) {
266 return error;
267 }
268
269 std::string size_value;
270 size_t size_index;
271 error =
272 GetValueStringAndValuePos(output, "size", 0, &size_value, &size_index);
273 if (error == EINVAL) {
274 size_value = "0";
275 } else if (error) {
276 return error;
277 }
278
279 // a Google Drive file has a max size of max of int32_t by design
280 // so the program logic is simpler when off_t in some platform is
281 // 32 bit and off_t in another platform is 64 bit.
282 *out_size = atoi(size_value.c_str());
binji 2016/09/20 01:22:04 you should validate this string. Try using strtoul
chanpatorikku 2016/09/21 16:41:09 strtoul(..) does not always validate the input cor
binji 2016/09/26 19:11:12 No, strtoul doesn't validate but you can tell wher
283
284 return 0;
285 }
286
287 Error GoogleDriveFsNode::Init(int open_flags) {
288 Error error = Node::Init(open_flags);
289 if (error) {
290 return error;
291 }
292
293 if (!filesystem_->ppapi()) {
294 return ENOSYS;
295 }
296
297 if (path_.IsRoot()) {
298 item_id_ = "root";
299 SetType(S_IFDIR);
300 SetMode(S_IREAD);
301 return 0;
302 }
303
304 error = RequestParentDirId(path_, filesystem_, &parent_dir_id_);
305 if (error) {
306 return error;
307 }
308
309 // Request the ID of an item, which is a file or a directory,
310 // and the item type.
311 bool is_dir_type;
312 error = RequestItemIdAndItemType(parent_dir_id_, path_.Basename(),
313 filesystem_, &item_id_, &is_dir_type);
314
315 if (error == ENOENT) {
316 // Only files are open as mode O_CREAT
317 if ((open_flags & O_CREAT) != 0) {
318 error = CreateEmptyFile();
319 if (error) {
320 return error;
321 }
322 error = RequestItemIdAndItemType(parent_dir_id_, path_.Basename(),
323 filesystem_, &item_id_, &is_dir_type);
324 if (error) {
325 return error;
326 }
327 SetType(S_IFREG);
328 if ((open_flags & O_RDWR) != 0) {
329 SetMode(S_IREAD | S_IWRITE);
330 } else if ((open_flags & O_WRONLY) != 0) {
331 SetMode(S_IWRITE);
332 } else {
333 SetMode(S_IREAD);
334 }
335 } else {
336 return ENOENT;
337 }
338 } else if (error) {
339 return error;
340 } else {
341 if (is_dir_type) {
342 SetType(S_IFDIR);
343 SetMode(S_IREAD);
344 } else {
345 SetType(S_IFREG);
346 if ((open_flags & O_RDWR) != 0) {
347 SetMode(S_IREAD | S_IWRITE);
348 } else if ((open_flags & O_WRONLY) != 0) {
349 SetMode(S_IWRITE);
350 } else {
351 SetMode(S_IREAD);
352 }
353 }
354
355 if (open_flags == 0) {
356 // open_flags == 0 for file opened on fopen with mode r and
357 // directory opened on opendir
358 return 0;
359 } else if (IsaDir()) {
360 return EPERM;
361 } else if ((open_flags & O_TRUNC) != 0) {
362 error = WriteHelper(NULL, 0);
363 if (error) {
364 return error;
365 }
366 }
367 }
368
369 return 0;
370 }
371
372 Error GoogleDriveFsNode::ReadHelper(int32_t start,
373 int32_t end,
374 void* out_buffer,
375 int32_t* out_bytes) {
376 memset(out_buffer, 0, 1);
binji 2016/09/20 01:22:05 why?
chanpatorikku 2016/09/21 16:41:09 The out variables of many functions in the nacl_io
binji 2016/09/26 19:11:12 OK, but it seems strange to null-terminate out_buf
377 *out_bytes = 0;
378
379 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
380
381 RequestUrlParams p;
382
383 p.url = DOWNLOAD_DRIVE_URL;
384 AddUrlPath(item_id_, &p.url);
385 AddUrlFirstQueryParameter("alt", "media", &p.url);
386
387 p.method = "GET";
388
389 AddHeaders("Content-type", "application/json", &p.headers);
390 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
391
392 char range_value_buffer[1024];
393 snprintf(range_value_buffer, sizeof(range_value_buffer), "bytes=%i-%i", start,
394 end);
395
396 AddHeaders("Range", range_value_buffer, &p.headers);
397
398 ScopedResource url_response_info_resource(googledrivefs->ppapi());
399 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
400 if (error) {
401 return error;
402 }
403
404 int32_t status_code = ReadStatusCode(
405 googledrivefs->ppapi(), url_response_info_resource.pp_resource());
406 if (status_code == STATUSCODE_OK ||
407 status_code == STATUSCODE_PARTIAL_CONTENT) {
408 std::string output;
409 error = ReadResponseBody(googledrivefs->ppapi(),
410 url_response_info_resource.pp_resource(),
411 end - start + 1, &output);
412 if (error) {
413 return error;
414 }
415
416 memcpy(out_buffer, &output[0], output.size());
417 *out_bytes = output.size();
418
419 return 0;
420 } else if (status_code == STATUSCODE_REQUESTED_RANGE_NOT_SATISFIABLE) {
421 return 0;
422 }
423
424 return EPERM;
425 }
426
427 Error GoogleDriveFsNode::WriteHelper(const char* body_data, int32_t body_size) {
428 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
429
430 RequestUrlParams p;
431
432 p.url = UPLOAD_DRIVE_URL;
433 AddUrlPath(item_id_, &p.url);
434 AddUrlFirstQueryParameter("uploadType", "media", &p.url);
435
436 p.method = "PATCH";
437
438 AddHeaders("Content-type", "application/json", &p.headers);
439 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
440
441 p.body = std::string(body_data, body_size);
442
443 ScopedResource url_response_info_resource(googledrivefs->ppapi());
444 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
445 if (error) {
446 return error;
447 }
448
449 if (ReadStatusCode(googledrivefs->ppapi(),
450 url_response_info_resource.pp_resource()) !=
451 STATUSCODE_OK) {
452 return EPERM;
453 }
454
455 return 0;
456 }
457
458 Error GoogleDriveFsNode::GetModifiedTime(time_t* out_mtime) {
459 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
460
461 RequestUrlParams p;
462
463 p.url = DRIVE_URL;
464 AddUrlPath(item_id_, &p.url);
465 AddUrlFirstQueryParameter("fields", "modifiedTime", &p.url);
466
467 p.method = "GET";
468
469 AddHeaders("Content-type", "application/json", &p.headers);
470 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
471
472 ScopedResource url_response_info_resource(googledrivefs->ppapi());
473 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
474 if (error) {
475 return error;
476 }
477
478 if (ReadStatusCode(googledrivefs->ppapi(),
479 url_response_info_resource.pp_resource()) !=
480 STATUSCODE_OK) {
481 return EPERM;
482 }
483
484 std::string output;
485 error = ReadResponseBody(googledrivefs->ppapi(),
486 url_response_info_resource.pp_resource(),
487 std::numeric_limits<int32_t>::max(), &output);
488 if (error) {
489 return error;
490 }
491
492 std::string modified_time_value;
493 size_t modified_time_index;
494 error = GetValueStringAndValuePos(output, "modifiedTime", 0,
495 &modified_time_value, &modified_time_index);
496 if (error) {
497 return EPERM;
498 }
499
500 *out_mtime =
501 ConvertDateTimeToEpochTime(ExtractYearFromRFC3339(modified_time_value),
502 ExtractMonthFromRFC3339(modified_time_value),
503 ExtractDayFromRFC3339(modified_time_value),
504 ExtractHourFromRFC3339(modified_time_value),
505 ExtractMinuteFromRFC3339(modified_time_value),
506 ExtractSecondFromRFC3339(modified_time_value));
507
508 return 0;
509 }
510
511 Error GoogleDriveFsNode::CreateEmptyFile() {
512 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
513
514 RequestUrlParams p;
515 std::string BOUNDARY_VALUE = "foo_bar_baz";
516
517 p.url = UPLOAD_DRIVE_URL;
518 AddUrlFirstQueryParameter("uploadType", "multipart", &p.url);
519
520 p.method = "POST";
521
522 AddHeaders("Content-type", "multipart/related; boundary=" + BOUNDARY_VALUE,
binji 2016/09/20 01:22:04 I think a nicer API would be: p.AddHeaders(...);
chanpatorikku 2016/09/21 16:41:09 AddHeaders, AddBody, RequestUrlParams, and so on a
binji 2016/09/26 19:11:12 No, this isn't necessary, just mentioned it becaus
523 &p.headers);
524 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
525
526 AddBody("--" + BOUNDARY_VALUE, &p.body);
527 AddBody("Content-Type: application/json; charset=UTF-8", &p.body);
528 AddBody("", &p.body);
529 AddBody("{", &p.body);
530 AddBody(" \"name\": \"" + path_.Basename() + "\",", &p.body);
531 AddBody(" \"parents\": [", &p.body);
532 AddBody(" \"" + parent_dir_id_ + "\"", &p.body);
533 AddBody(" ]", &p.body);
534 AddBody("}", &p.body);
535 AddBody("", &p.body);
536 AddBody("--" + BOUNDARY_VALUE, &p.body);
537 AddBody("Content-Type: text/plain", &p.body);
538 AddBody("", &p.body);
539 AddBody("", &p.body);
540 AddBody("--" + BOUNDARY_VALUE + "--", &p.body);
541
542 ScopedResource url_response_info_resource(googledrivefs->ppapi());
543 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
544 if (error) {
545 return error;
546 }
547
548 if (ReadStatusCode(googledrivefs->ppapi(),
549 url_response_info_resource.pp_resource()) !=
550 STATUSCODE_OK) {
551 return EPERM;
552 }
553
554 return 0;
555 }
556
557 Error GoogleDriveFsNode::RequestDirent(
558 const std::string& optional_page_token,
559 std::vector<std::string>* out_dirent_names) {
560 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
561
562 RequestUrlParams p;
563
564 p.url = DRIVE_URL;
565 AddUrlFirstQueryParameter("q", ParentEqualClause(item_id_), &p.url);
566
567 if (!optional_page_token.empty()) {
568 AddUrlNextQueryParameter("pageToken", optional_page_token, &p.url);
569 }
570
571 p.method = "GET";
572
573 AddHeaders("Content-type", "application/json", &p.headers);
574 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
575
576 ScopedResource url_response_info_resource(googledrivefs->ppapi());
577 Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
578 if (error) {
579 return error;
580 }
581
582 if (ReadStatusCode(googledrivefs->ppapi(),
583 url_response_info_resource.pp_resource()) !=
584 STATUSCODE_OK) {
585 return EPERM;
586 }
587
588 std::string output;
589 error = ReadResponseBody(googledrivefs->ppapi(),
590 url_response_info_resource.pp_resource(),
591 std::numeric_limits<int32_t>::max(), &output);
592 if (error) {
593 return error;
594 }
595
596 std::string name_value;
597 size_t name_index;
598 error =
599 GetValueStringAndValuePos(output, "name", 0, &name_value, &name_index);
600 if (error && error != EINVAL) {
601 return error;
602 }
603
604 while (!error) {
605 out_dirent_names->push_back(name_value);
606
607 error = GetValueStringAndValuePos(output, "name", name_index, &name_value,
608 &name_index);
609 if (error && error != EINVAL) {
610 return error;
611 }
612 }
613
614 std::string next_page_token_value;
615 size_t next_page_token_index;
616 error =
617 GetValueStringAndValuePos(output, "nextPageToken", 0,
618 &next_page_token_value, &next_page_token_index);
619 if (!error) {
620 return RequestDirent(next_page_token_value, out_dirent_names);
621 } else if (error != EINVAL) {
622 return error;
623 }
624
625 return 0;
626 }
627
628 } // namespace nacl_io
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698