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 // 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 | |
OLD | NEW |