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

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, 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 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..c38f2c74e67c33ad9a6bf5d5516fc7be5fb5339e
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc
@@ -0,0 +1,628 @@
+// 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+#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("..")));
+
+ 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;
+ }
+
+ if (attr.offs > file_size ||
+ attr.offs >= std::numeric_limits<int32_t>::max()) {
+ return EFBIG;
+ }
+
+ // 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
+ // so the program logic is simpler when off_t in some platform is
+ // 32 bit and off_t in another platform is 64 bit.
+ // *out_bytes is in int. Suppose int is >= 32 bits, figure the number of
+ // necessary bytes to write so after GoogleDriveFsNode::Write(..),
+ // the Google Drive file size is <= max of int32_t.
+ int bytes_to_write =
+ std::min<size_t>(count, std::numeric_limits<int32_t>::max());
+ bytes_to_write = std::min<int32_t>(
+ bytes_to_write, std::numeric_limits<int32_t>::max() - attr.offs);
+
+ int32_t file_buffer_size =
+ std::max<int32_t>(file_size, attr.offs + bytes_to_write);
+
+ // 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) {
+ 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
+ error =
+ 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
+ if (error) {
+ return error;
+ }
+ }
+
+ 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
+
+ error = WriteHelper(file_buffer.c_str(), file_buffer_size);
+ if (error) {
+ return error;
+ }
+
+ *out_bytes = bytes_to_write;
+
+ return 0;
+}
+
+Error GoogleDriveFsNode::FTruncate(off_t length) {
+ if (IsaDir()) {
+ return EISDIR;
+ }
+
+ // A Google Drive file size is <= max of int32_t by design
+ // so the program logic is simpler when off_t in some platform is
+ // 32 bit and off_t in another platform is 64 bit.
+ // Make sure length <= max of int32_t so after
+ // GoogleDriveFsNode::FTruncate(..), the Google Drive file size
+ // is <= max of int32_t.
+ if (length > std::numeric_limits<int32_t>::max()) {
+ return EFBIG;
+ }
+
+ off_t file_size;
+ Error error = GetSize(&file_size);
+ if (error) {
+ return error;
+ }
+
+ std::string file_buffer(length, '\0');
+
+ if (file_size > 0) {
+ int32_t read_helper_out_bytes;
+ int32_t read_end = std::min<int32_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;
+ }
+
+ if (count == 0) {
chanpatorikku 2016/09/06 14:49:38 Return 0 when count == 0 so the later code: Err
+ return 0;
+ }
+
+ // a Google Drive file has a max size of max of int32_t by design
+ // so the program logic is simpler when off_t in some platform is
+ // 32 bit and off_t in another platform is 64 bit.
+ // Google Drive files written from nacl_io can be only up
+ // to INT32_MAX bytes.
+ if (attr.offs >= std::numeric_limits<int32_t>::max()) {
+ return 0;
+ }
+
+ // Suppose int is >= 32 bits, bytes_to_read is adjusted to
+ // be in the range of a Google Drive file.
+ int bytes_to_read =
+ std::min<size_t>(count, std::numeric_limits<int32_t>::max());
+ bytes_to_read = std::min<int32_t>(
+ bytes_to_read, std::numeric_limits<int32_t>::max() - attr.offs);
+
+ int32_t read_helper_out_bytes;
+ Error error = ReadHelper(attr.offs, attr.offs + bytes_to_read - 1, buf,
+ &read_helper_out_bytes);
+ if (error) {
+ return error;
+ }
+
+ *out_bytes = read_helper_out_bytes;
+
+ 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(),
+ std::numeric_limits<int32_t>::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;
+ }
+
+ // a Google Drive file has a max size of max of int32_t by design
+ // so the program logic is simpler when off_t in some platform is
+ // 32 bit and off_t in another platform is 64 bit.
+ *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
+
+ 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(int32_t start,
+ int32_t end,
+ void* out_buffer,
+ int32_t* out_bytes) {
+ 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
+ *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];
+ snprintf(range_value_buffer, sizeof(range_value_buffer), "bytes=%i-%i", start,
+ end);
+
+ 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;
+ }
+
+ memcpy(out_buffer, &output[0], output.size());
+ *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, int32_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(),
+ std::numeric_limits<int32_t>::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,
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
+ &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(),
+ std::numeric_limits<int32_t>::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