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

Unified 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, 4 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 side-by-side diff with in-line comments
Download patch
Index: native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc
diff --git a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc
new file mode 100644
index 0000000000000000000000000000000000000000..af16c81e08de51f4ded1b040546f10ab503f9e08
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc
@@ -0,0 +1,609 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "nacl_io/googledrivefs/googledrivefs_node.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "ppapi/c/pp_completion_callback.h"
+
+#include "nacl_io/error.h"
+#include "nacl_io/filesystem.h"
+#include "nacl_io/getdents_helper.h"
+#include "nacl_io/hash.h"
+#include "nacl_io/kernel_handle.h"
+#include "nacl_io/statuscode.h"
+#include "nacl_io/googledrivefs/googledrivefs.h"
+#include "nacl_io/googledrivefs/googledrivefs_util.h"
+
+namespace nacl_io {
+
+GoogleDriveFsNode::GoogleDriveFsNode(Filesystem* filesystem, Path path)
+ : Node(filesystem), path_(path) {}
+
+Error GoogleDriveFsNode::GetDents(size_t offs,
+ struct dirent* pdir,
+ size_t size,
+ int* out_bytes) {
+ *out_bytes = 0;
+
+ if (!IsaDir()) {
+ return ENOTDIR;
+ }
+
+ 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.
+
+ std::vector<std::string> dirent_names;
+ Error error = RequestDirent("", &dirent_names);
+ if (error) {
+ return error;
+ }
+
+ for (size_t i = 0; i < dirent_names.size(); ++i) {
+ Path child_path(path_);
+ child_path = child_path.Append("/" + dirent_names[i]);
+ ino_t child_ino = HashPath(child_path);
+
+ helper.AddDirent(child_ino, dirent_names[i].c_str(),
+ dirent_names[i].size());
+ }
+
+ return helper.GetDents(offs, pdir, size, out_bytes);
+}
+
+Error GoogleDriveFsNode::GetStat(struct stat* pstat) {
+ Error error = GetSize(&pstat->st_size);
+ if (error) {
+ return error;
+ }
+
+ error = GetModifiedTime(&pstat->st_mtime);
+ if (error) {
+ return error;
+ }
+
+ pstat->st_atime = 0;
+ pstat->st_ctime = 0;
+
+ pstat->st_mode = stat_.st_mode;
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::Write(const HandleAttr& attr,
+ const void* buf,
+ size_t count,
+ int* out_bytes) {
+ *out_bytes = 0;
+
+ if (IsaDir()) {
+ return EISDIR;
+ }
+ if ((GetMode() & S_IWRITE) == 0) {
+ return EACCES;
+ }
+
+ off_t file_size;
+ Error error = GetSize(&file_size);
+ if (error) {
+ return error;
+ }
+
+ // file_size is <= UINT_MAX. Google Drive API v3 supports only
+ // file overwrite, and URLRequestInfoInterface::AppendDataToBody(..)
+ // can write up to the max number of uint32_t, so a Google Drive
+ // file has a max size of UINT_MAX.
+ // Assert attr.offs + count <= UINT_MAX so after
+ // GoogleDriveFsNode::Write(..), the Google Drive file size is <= UINT_MAX.
+ 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
+
+ uint32_t file_buffer_size = std::max<uint32_t>(file_size, attr.offs + count);
+
+ // use std::string for storing data in the heap, as the size of stack
+ // is measured in megabytes, disallowing files larger than that.
+ std::string file_buffer(file_buffer_size, '\0');
+
+ if (file_size > 0) {
+ uint32_t read_helper_out_bytes;
+ error =
+ ReadHelper(0, file_size - 1, &file_buffer[0], &read_helper_out_bytes);
+ if (error) {
+ return error;
+ }
+ }
+
+ 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.
+
+ error = WriteHelper(file_buffer.c_str(), file_buffer_size);
+ if (error) {
+ return error;
+ }
+
+ *out_bytes = count;
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::FTruncate(off_t length) {
+ if (IsaDir()) {
+ return EISDIR;
+ }
+
+ off_t file_size;
+ Error error = GetSize(&file_size);
+ if (error) {
+ return error;
+ }
+
+ // a Google Drive file size is <= UINT_MAX. Google Drive API v3
+ // supports only file overwrite, and
+ // URLRequestInfoInterface::AppendDataToBody(..)
+ // can write up to the max number of uint32_t, so a Google Drive
+ // file has a max size of UINT_MAX.
+ // Assert length <= UINT_MAX so after GoogleDriveFsNode::FTruncate(..),
+ // the Google Drive file size is <= UINT_MAX.
+ 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
+
+ std::string file_buffer(length, '\0');
+
+ if (file_size > 0) {
+ uint32_t read_helper_out_bytes;
+ uint32_t read_end = std::min<uint32_t>(length, file_size);
+ error =
+ ReadHelper(0, read_end - 1, &file_buffer[0], &read_helper_out_bytes);
+ if (error) {
+ return error;
+ }
+ }
+
+ error = WriteHelper(file_buffer.c_str(), length);
+ if (error) {
+ return error;
+ }
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::Read(const HandleAttr& attr,
+ void* buf,
+ size_t count,
+ int* out_bytes) {
+ *out_bytes = 0;
+
+ if (IsaDir()) {
+ return EISDIR;
+ }
+ if ((GetMode() & S_IREAD) == 0) {
+ return EACCES;
+ }
+
+ // GoogleDriveFsNode::ReadHelper(..) can read only up to UINT_MAX bytes.
+ if (attr.offs > UINT_MAX) {
+ 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
+ }
+
+ // GoogleDriveFsNode::Read(..) can read only up to INT_MAX bytes.
+ int bytes_to_read = std::min<size_t>(INT_MAX, count);
+
+ // GoogleDriveFsNode::ReadHelper(..) can read only up to UINT_MAX bytes.
+ uint32_t read_end =
+ std::min<uint64_t>(attr.offs + bytes_to_read - 1, UINT_MAX);
+
+ Error error =
+ 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.
+ if (error) {
+ return error;
+ }
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::GetSize(off_t* out_size) {
+ *out_size = 0;
+
+ if (IsaDir()) {
+ return 0;
+ }
+
+ GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
+
+ RequestUrlParams p;
+
+ p.url = DRIVE_URL;
+ AddUrlPath(item_id_, &p.url);
+ AddUrlFirstQueryParameter("fields", "size", &p.url);
+
+ p.method = "GET";
+
+ AddHeaders("Content-type", "application/json", &p.headers);
+ AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
+
+ ScopedResource url_response_info_resource(googledrivefs->ppapi());
+ Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
+ if (error) {
+ return error;
+ }
+
+ if (ReadStatusCode(googledrivefs->ppapi(),
+ url_response_info_resource.pp_resource()) !=
+ STATUSCODE_OK) {
+ return EPERM;
+ }
+
+ std::string output;
+ error = ReadResponseBody(googledrivefs->ppapi(),
+ url_response_info_resource.pp_resource(), UINT_MAX,
+ &output);
+ if (error) {
+ return error;
+ }
+
+ std::string size_value;
+ size_t size_index;
+ error =
+ GetValueStringAndValuePos(output, "size", 0, &size_value, &size_index);
+ if (error == EINVAL) {
+ size_value = "0";
+ } else if (error) {
+ return error;
+ }
+
+ *out_size = (off_t)atoi(size_value.c_str());
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::Init(int open_flags) {
+ Error error = Node::Init(open_flags);
+ if (error) {
+ return error;
+ }
+
+ if (!filesystem_->ppapi()) {
+ return ENOSYS;
+ }
+
+ if (path_.IsRoot()) {
+ item_id_ = "root";
+ SetType(S_IFDIR);
+ SetMode(S_IREAD);
+ return 0;
+ }
+
+ error = RequestParentDirId(path_, filesystem_, &parent_dir_id_);
+ if (error) {
+ return error;
+ }
+
+ // Request the ID of an item, which is a file or a directory,
+ // and the item type.
+ bool is_dir_type;
+ error = RequestItemIdAndItemType(parent_dir_id_, path_.Basename(),
+ filesystem_, &item_id_, &is_dir_type);
+
+ if (error == ENOENT) {
+ // Only files are open as mode O_CREAT
+ if ((open_flags & O_CREAT) != 0) {
+ error = CreateEmptyFile();
+ if (error) {
+ return error;
+ }
+ error = RequestItemIdAndItemType(parent_dir_id_, path_.Basename(),
+ filesystem_, &item_id_, &is_dir_type);
+ if (error) {
+ return error;
+ }
+ SetType(S_IFREG);
+ if ((open_flags & O_RDWR) != 0) {
+ SetMode(S_IREAD | S_IWRITE);
+ } else if ((open_flags & O_WRONLY) != 0) {
+ SetMode(S_IWRITE);
+ } else {
+ SetMode(S_IREAD);
+ }
+ } else {
+ return ENOENT;
+ }
+ } else if (error) {
+ return error;
+ } else {
+ if (is_dir_type) {
+ SetType(S_IFDIR);
+ SetMode(S_IREAD);
+ } else {
+ SetType(S_IFREG);
+ if ((open_flags & O_RDWR) != 0) {
+ SetMode(S_IREAD | S_IWRITE);
+ } else if ((open_flags & O_WRONLY) != 0) {
+ SetMode(S_IWRITE);
+ } else {
+ SetMode(S_IREAD);
+ }
+ }
+
+ if (open_flags == 0) {
+ // open_flags == 0 for file opened on fopen with mode r and
+ // directory opened on opendir
+ return 0;
+ } else if (IsaDir()) {
+ return EPERM;
+ } else if ((open_flags & O_TRUNC) != 0) {
+ error = WriteHelper(NULL, 0);
+ if (error) {
+ return error;
+ }
+ }
+ }
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::ReadHelper(uint32_t start,
+ uint32_t end,
+ 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.
+ uint32_t* out_bytes) {
+ 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.
+ *out_bytes = 0;
+
+ GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
+
+ RequestUrlParams p;
+
+ p.url = DOWNLOAD_DRIVE_URL;
+ AddUrlPath(item_id_, &p.url);
+ AddUrlFirstQueryParameter("alt", "media", &p.url);
+
+ p.method = "GET";
+
+ AddHeaders("Content-type", "application/json", &p.headers);
+ AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
+
+ char range_value_buffer[1024];
+ int char_written = snprintf(range_value_buffer, sizeof(range_value_buffer),
+ "bytes=%u-%u", start, end);
+
+ if (char_written < 0 ||
+ 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.
+ return EPERM;
+ }
+
+ AddHeaders("Range", range_value_buffer, &p.headers);
+
+ ScopedResource url_response_info_resource(googledrivefs->ppapi());
+ Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
+ if (error) {
+ return error;
+ }
+
+ int32_t status_code = ReadStatusCode(
+ googledrivefs->ppapi(), url_response_info_resource.pp_resource());
+ if (status_code == STATUSCODE_OK ||
+ status_code == STATUSCODE_PARTIAL_CONTENT) {
+ std::string output;
+ error = ReadResponseBody(googledrivefs->ppapi(),
+ url_response_info_resource.pp_resource(),
+ end - start + 1, &output);
+ if (error) {
+ return error;
+ }
+
+ 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
+ *out_bytes = output.size();
+
+ return 0;
+ } else if (status_code == STATUSCODE_REQUESTED_RANGE_NOT_SATISFIABLE) {
+ return 0;
+ }
+
+ return EPERM;
+}
+
+Error GoogleDriveFsNode::WriteHelper(const char* body_data,
+ uint32_t body_size) {
+ GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
+
+ RequestUrlParams p;
+
+ p.url = UPLOAD_DRIVE_URL;
+ AddUrlPath(item_id_, &p.url);
+ AddUrlFirstQueryParameter("uploadType", "media", &p.url);
+
+ p.method = "PATCH";
+
+ AddHeaders("Content-type", "application/json", &p.headers);
+ AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
+
+ p.body = std::string(body_data, body_size);
+
+ ScopedResource url_response_info_resource(googledrivefs->ppapi());
+ Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
+ if (error) {
+ return error;
+ }
+
+ if (ReadStatusCode(googledrivefs->ppapi(),
+ url_response_info_resource.pp_resource()) !=
+ STATUSCODE_OK) {
+ return EPERM;
+ }
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::GetModifiedTime(time_t* out_mtime) {
+ GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
+
+ RequestUrlParams p;
+
+ p.url = DRIVE_URL;
+ AddUrlPath(item_id_, &p.url);
+ AddUrlFirstQueryParameter("fields", "modifiedTime", &p.url);
+
+ p.method = "GET";
+
+ AddHeaders("Content-type", "application/json", &p.headers);
+ AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
+
+ ScopedResource url_response_info_resource(googledrivefs->ppapi());
+ Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
+ if (error) {
+ return error;
+ }
+
+ if (ReadStatusCode(googledrivefs->ppapi(),
+ url_response_info_resource.pp_resource()) !=
+ STATUSCODE_OK) {
+ return EPERM;
+ }
+
+ std::string output;
+ error = ReadResponseBody(googledrivefs->ppapi(),
+ url_response_info_resource.pp_resource(), UINT_MAX,
+ &output);
+ if (error) {
+ return error;
+ }
+
+ std::string modified_time_value;
+ size_t modified_time_index;
+ error = GetValueStringAndValuePos(output, "modifiedTime", 0,
+ &modified_time_value, &modified_time_index);
+ if (error) {
+ return EPERM;
+ }
+
+ *out_mtime =
+ ConvertDateTimeToEpochTime(ExtractYearFromRFC3339(modified_time_value),
+ ExtractMonthFromRFC3339(modified_time_value),
+ ExtractDayFromRFC3339(modified_time_value),
+ ExtractHourFromRFC3339(modified_time_value),
+ ExtractMinuteFromRFC3339(modified_time_value),
+ ExtractSecondFromRFC3339(modified_time_value));
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::CreateEmptyFile() {
+ GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
+
+ RequestUrlParams p;
+ std::string BOUNDARY_VALUE = "foo_bar_baz";
+
+ p.url = UPLOAD_DRIVE_URL;
+ AddUrlFirstQueryParameter("uploadType", "multipart", &p.url);
+
+ p.method = "POST";
+
+ AddHeaders("Content-type", "multipart/related; boundary=" + BOUNDARY_VALUE,
+ &p.headers);
+ AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
+
+ AddBody("--" + BOUNDARY_VALUE, &p.body);
+ AddBody("Content-Type: application/json; charset=UTF-8", &p.body);
+ AddBody("", &p.body);
+ AddBody("{", &p.body);
+ AddBody(" \"name\": \"" + path_.Basename() + "\",", &p.body);
+ AddBody(" \"parents\": [", &p.body);
+ AddBody(" \"" + parent_dir_id_ + "\"", &p.body);
+ AddBody(" ]", &p.body);
+ AddBody("}", &p.body);
+ AddBody("", &p.body);
+ AddBody("--" + BOUNDARY_VALUE, &p.body);
+ AddBody("Content-Type: text/plain", &p.body);
+ AddBody("", &p.body);
+ AddBody("", &p.body);
+ AddBody("--" + BOUNDARY_VALUE + "--", &p.body);
+
+ ScopedResource url_response_info_resource(googledrivefs->ppapi());
+ Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
+ if (error) {
+ return error;
+ }
+
+ if (ReadStatusCode(googledrivefs->ppapi(),
+ url_response_info_resource.pp_resource()) !=
+ STATUSCODE_OK) {
+ return EPERM;
+ }
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::RequestDirent(
+ const std::string& optional_page_token,
+ std::vector<std::string>* out_dirent_names) {
+ GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem_);
+
+ RequestUrlParams p;
+
+ p.url = DRIVE_URL;
+ AddUrlFirstQueryParameter("q", ParentEqualClause(item_id_), &p.url);
+
+ if (!optional_page_token.empty()) {
+ AddUrlNextQueryParameter("pageToken", optional_page_token, &p.url);
+ }
+
+ p.method = "GET";
+
+ AddHeaders("Content-type", "application/json", &p.headers);
+ AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers);
+
+ ScopedResource url_response_info_resource(googledrivefs->ppapi());
+ Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource);
+ if (error) {
+ return error;
+ }
+
+ if (ReadStatusCode(googledrivefs->ppapi(),
+ url_response_info_resource.pp_resource()) !=
+ STATUSCODE_OK) {
+ return EPERM;
+ }
+
+ std::string output;
+ error = ReadResponseBody(googledrivefs->ppapi(),
+ url_response_info_resource.pp_resource(), UINT_MAX,
+ &output);
+ if (error) {
+ return error;
+ }
+
+ std::string name_value;
+ size_t name_index;
+ error =
+ GetValueStringAndValuePos(output, "name", 0, &name_value, &name_index);
+ if (error && error != EINVAL) {
+ return error;
+ }
+
+ while (!error) {
+ out_dirent_names->push_back(name_value);
+
+ error = GetValueStringAndValuePos(output, "name", name_index, &name_value,
+ &name_index);
+ if (error && error != EINVAL) {
+ return error;
+ }
+ }
+
+ std::string next_page_token_value;
+ size_t next_page_token_index;
+ error =
+ GetValueStringAndValuePos(output, "nextPageToken", 0,
+ &next_page_token_value, &next_page_token_index);
+ if (!error) {
+ return RequestDirent(next_page_token_value, out_dirent_names);
+ } else if (error != EINVAL) {
+ return error;
+ }
+
+ return 0;
+}
+
+} // namespace nacl_io

Powered by Google App Engine
This is Rietveld 408576698