Index: native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.cc |
diff --git a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.cc b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..430714c822c5194c971725aec89f62fb0fa285e0 |
--- /dev/null |
+++ b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.cc |
@@ -0,0 +1,1346 @@ |
+// 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.h" |
+ |
+#include <fcntl.h> |
+ |
+#include <map> |
+#include <algorithm> |
+#include <cstdio> |
+ |
+#include "ppapi/cpp/url_request_info.h" |
+#include "ppapi/cpp/url_response_info.h" |
+#include "ppapi/cpp/url_loader.h" |
+#include "ppapi/cpp/file_ref.h" |
+#include "ppapi/cpp/file_io.h" |
+ |
+#include "nacl_io/error.h" |
+#include "nacl_io/pepper_interface.h" |
+#include "ppapi/c/pp_completion_callback.h" |
+ |
+#include "json/reader.h" |
binji
2016/07/18 23:24:02
nacl_io currently does not depend on json; We're n
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
Not going to add it as a new depend
|
+#include "json/writer.h" |
+ |
+#include "nacl_io/filesystem.h" |
+#include "nacl_io/kernel_handle.h" |
+#include "nacl_io/getdents_helper.h" |
+ |
+#ifdef WIN32 |
+#include <windows.h> |
binji
2016/07/18 23:24:02
what is this needed for?
chanpatorikku
2016/08/07 02:41:02
This used to be needed for Sleep() from windows.h.
|
+#else |
+#include <unistd.h> |
+#endif |
+ |
+namespace nacl_io { |
+ |
+namespace { |
+ |
+std::string MapToHeaders( |
+ const std::map<std::string, std::string>& header_field_map) { |
+ std::string request_headers = ""; |
+ |
+ for (std::map<std::string, std::string>::const_iterator it = |
+ header_field_map.begin(); |
+ it != header_field_map.end(); ++it) { |
+ request_headers += it->first; |
+ request_headers += ": "; |
+ request_headers += it->second; |
+ request_headers += "\n"; |
+ } |
+ return request_headers; |
+} |
+ |
+// Continuing DJB2a hash |
+ino_t HashPathSegment(ino_t hash, const char* str, size_t len) { |
+ // First add the path seperator |
+ hash = (hash * static_cast<ino_t>(33)) ^ '/'; |
+ while (len--) { |
+ hash = (hash * static_cast<ino_t>(33)) ^ *str++; |
+ } |
+ return hash; |
+} |
+ |
+ino_t HashPath(const Path& path) { |
binji
2016/07/18 23:24:02
better to share the code with Html5Fs rather than
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
Shared the code with Html5Fs.
|
+ // Prime the DJB2a hash |
+ ino_t hash = 5381; |
+ |
+ // Apply a running DJB2a to each part of the path |
+ for (size_t segment = 0; segment < path.Size(); segment++) { |
+ const std::string& part = path.Part(segment); |
+ hash = HashPathSegment(hash, part.c_str(), part.length()); |
+ } |
+ return hash; |
+} |
+ |
+// A Google Drive item is a file or a directory. |
+// RequestDirId requests to see if a directory with dir_name |
+// and a parent with ID parent_dir_id is there. |
+// If it is, dir_id is set. |
+// RequestDirId returns ENOENT when a file with dir_name is there |
+// but a directory with dir_name is not. |
+Error RequestDirId(const std::string& parent_dir_id, |
+ const std::string& dir_name, |
+ Filesystem* filesystem, |
+ std::string& dir_id) { |
+ dir_id = ""; |
+ |
+ std::string url = "https://www.googleapis.com/drive/v3/files"; |
binji
2016/07/18 23:24:02
It's better to have these strings be constants (as
chanpatorikku
2016/08/07 02:41:02
Each URL may be different.
I don't know if the cur
|
+ url += "?"; |
+ url += "q="; |
+ url += "%27"; |
+ url += parent_dir_id; |
+ url += "%27+in+parents"; |
+ url += "+and+"; |
+ url += "mimeType+=+"; |
+ url += "%27application/vnd.google-apps.folder%27"; |
+ url += "+and+"; |
+ url += "name+=+%27" + dir_name + "%27"; |
+ |
+ GoogleDriveFs* googledrivefs = dynamic_cast<GoogleDriveFs*>(filesystem); |
binji
2016/07/18 23:24:02
don't use dynamic_cast, it relies on RTTI. static_
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
static_cast is called in the curren
|
+ if (googledrivefs == NULL) { |
+ return EINVAL; |
+ } |
+ |
+ pp::URLLoader loader(pp::InstanceHandle(googledrivefs->instance())); |
binji
2016/07/18 23:24:02
nacl_io uses the Pepper C interface, not the C++ i
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
Pepper C interface is used in the c
|
+ pp::URLRequestInfo request(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ bool set_url_ok = request.SetURL(url); |
binji
2016/07/18 23:24:02
it's better to just inline this than to create a n
chanpatorikku
2016/08/07 02:41:02
I don't know exactly about inlining.
Please review
|
+ if (!set_url_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_method_ok = request.SetMethod("GET"); |
+ if (!set_method_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::map<std::string, std::string> headers_map; |
binji
2016/07/18 23:24:02
This seems to be duplicated a number of times in t
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
Tried factoring it in a similar way
|
+ headers_map["Content-type"] = "application/json"; |
+ std::string authorization_value = "Bearer "; |
+ authorization_value += googledrivefs->token(); |
+ headers_map["Authorization"] = authorization_value; |
+ |
+ std::string headers = MapToHeaders(headers_map); |
+ |
+ bool set_headers_ok = request.SetHeaders(headers); |
+ if (!set_headers_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_allow_cross_origin_requests_ok = |
+ request.SetAllowCrossOriginRequests(true); |
+ if (!set_allow_cross_origin_requests_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_stream_to_file_ok = request.SetStreamToFile(true); |
+ if (!set_stream_to_file_ok) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = loader.Open(request, pp::BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ pp::URLResponseInfo response = loader.GetResponseInfo(); |
+ if (response.GetStatusCode() != 200) { |
+ return EPERM; |
+ } |
+ |
+ pp::FileRef file_ref = response.GetBodyAsFileRef(); |
+ pp::FileIO file_io(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ int32_t open_result = |
+ file_io.Open(file_ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete()); |
+ if (open_result < 0) { |
+ return PPERROR_TO_ERRNO(open_result); |
+ } |
+ |
+ int BUFFER_SIZE = 1024; |
+ char buffer[BUFFER_SIZE]; |
binji
2016/07/18 23:24:02
What if the result is greater than 1024 bytes?
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
Code is rewritten for the result gr
|
+ |
+ int32_t byte_read = |
+ file_io.Read(0, buffer, BUFFER_SIZE, pp::BlockUntilComplete()); |
+ while (byte_read == 0) { |
binji
2016/07/18 23:24:02
This loop is strange, what problem are you trying
chanpatorikku
2016/08/07 02:41:02
Done.
The problem I was trying to fix was that Fi
|
+#ifdef WIN32 |
+ Sleep(1000); |
+#else |
+ usleep(1000000); |
binji
2016/07/18 23:24:02
We never want to call sleep in nacl_io, it's usual
chanpatorikku
2016/08/07 02:41:02
Done.
Sleep is not called in nacl_io in the curre
|
+#endif |
+ |
+ byte_read = file_io.Read(0, buffer, BUFFER_SIZE, pp::BlockUntilComplete()); |
+ } |
+ |
+ if (byte_read < 0) { |
+ return PPERROR_TO_ERRNO(byte_read); |
+ } |
+ |
+ std::string output(buffer); |
+ |
+ Json::Value root; |
+ |
+ Json::Reader reader(Json::Features::strictMode()); |
+ if (!reader.parse(output, root, false)) { |
+ return EPERM; |
+ } |
+ |
+ if (!root.isMember("files")) { |
+ return EPERM; |
+ } |
+ |
+ Json::Value files_array = root["files"]; |
+ if (!files_array.isArray()) { |
+ return EPERM; |
+ } |
+ if (files_array.size() == 0) { |
+ return ENOENT; |
+ } |
+ |
+ dir_id = files_array[0]["id"].asString(); |
+ |
+ return 0; |
+} |
+ |
+// A Google Drive item is a file or a directory. |
+// RequestItemId requests to see if either a file with item_name |
+// or a directory with item_name is there. |
+// If it is, item_id is set. |
+Error RequestItemId(const std::string& parent_dir_id, |
+ const std::string& item_name, |
+ Filesystem* filesystem, |
+ std::string& item_id) { |
+ item_id = ""; |
+ |
+ std::string url = "https://www.googleapis.com/drive/v3/files"; |
+ url += "?"; |
+ url += "q="; |
+ url += "%27"; |
+ url += parent_dir_id; |
+ url += "%27+in+parents"; |
+ url += "+and+"; |
+ url += "name+=+%27" + item_name + "%27"; |
+ |
+ GoogleDriveFs* googledrivefs = dynamic_cast<GoogleDriveFs*>(filesystem); |
+ if (googledrivefs == NULL) { |
+ return EINVAL; |
+ } |
+ |
+ pp::URLLoader loader(pp::InstanceHandle(googledrivefs->instance())); |
+ pp::URLRequestInfo request(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ bool set_url_ok = request.SetURL(url); |
+ if (!set_url_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_method_ok = request.SetMethod("GET"); |
+ if (!set_method_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::map<std::string, std::string> headers_map; |
+ headers_map["Content-type"] = "application/json"; |
+ std::string authorization_value = "Bearer "; |
+ authorization_value += googledrivefs->token(); |
+ headers_map["Authorization"] = authorization_value; |
+ |
+ std::string headers = MapToHeaders(headers_map); |
+ |
+ bool set_headers_ok = request.SetHeaders(headers); |
+ if (!set_headers_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_allow_cross_origin_requests_ok = |
+ request.SetAllowCrossOriginRequests(true); |
+ if (!set_allow_cross_origin_requests_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_stream_to_file_ok = request.SetStreamToFile(true); |
+ if (!set_stream_to_file_ok) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = loader.Open(request, pp::BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ pp::URLResponseInfo response = loader.GetResponseInfo(); |
+ if (response.GetStatusCode() != 200) { |
+ return EPERM; |
+ } |
+ |
+ pp::FileRef file_ref = response.GetBodyAsFileRef(); |
+ pp::FileIO file_io(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ int32_t open_result = |
+ file_io.Open(file_ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete()); |
+ if (open_result < 0) { |
+ return PPERROR_TO_ERRNO(open_result); |
+ } |
+ |
+ int BUFFER_SIZE = 1024; |
+ char buffer[BUFFER_SIZE]; |
+ |
+ int32_t byte_read = |
+ file_io.Read(0, buffer, BUFFER_SIZE, pp::BlockUntilComplete()); |
+ while (byte_read == 0) { |
+#ifdef WIN32 |
+ Sleep(1000); |
+#else |
+ usleep(1000000); |
+#endif |
+ |
+ byte_read = file_io.Read(0, buffer, BUFFER_SIZE, pp::BlockUntilComplete()); |
+ } |
+ |
+ if (byte_read < 0) { |
+ return PPERROR_TO_ERRNO(byte_read); |
+ } |
+ |
+ std::string output(buffer); |
+ |
+ Json::Value root; |
+ |
+ Json::Reader reader(Json::Features::strictMode()); |
+ if (!reader.parse(output, root, false)) { |
+ return EPERM; |
+ } |
+ |
+ if (!root.isMember("files")) { |
+ return EPERM; |
+ } |
+ |
+ Json::Value files_array = root["files"]; |
+ |
+ if (!files_array.isArray()) { |
+ return EPERM; |
+ } |
+ if (files_array.size() == 0) { |
+ return ENOENT; |
+ } |
+ |
+ item_id = files_array[0]["id"].asString(); |
+ |
+ return 0; |
+} |
+ |
+Error RequestParentDirId(const Path& path, |
+ Filesystem* filesystem, |
+ std::string& parent_dir_id) { |
binji
2016/07/18 23:24:02
Out parameters should be pointers, not references,
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
Out parameters are pointers and are
|
+ parent_dir_id = ""; |
+ |
+ if (path.Size() < 2) { |
+ return EINVAL; |
+ } |
+ |
+ std::string helper_parent_dir_id = "root"; |
+ |
+ for (unsigned int i = 1; i < path.Size() - 1; ++i) { |
+ std::string dir_name = path.Range(i, i + 1); |
+ |
+ std::string dir_id = ""; |
+ Error error = |
+ RequestDirId(helper_parent_dir_id, dir_name, filesystem, dir_id); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ helper_parent_dir_id = dir_id; |
+ } |
+ parent_dir_id = helper_parent_dir_id; |
+ |
+ return 0; |
+} |
+} |
+ |
+class GoogleDriveFsNode : public Node { |
binji
2016/07/18 23:24:02
It's generally nicer when the nodes are defined in
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
Nodes are defined in a separate fil
|
+ public: |
+ GoogleDriveFsNode(Filesystem* filesystem, Path path) |
+ : Node(filesystem), path_(path) {} |
+ |
+ Error GetDents(size_t offs, |
+ struct dirent* pdir, |
+ size_t size, |
+ int* out_bytes) { |
+ if (!IsaDir()) { |
+ return ENOTDIR; |
+ } |
+ |
+ const ino_t kCurDirIno = -1; |
+ const ino_t kParentDirIno = -2; |
+ GetDentsHelper helper(kCurDirIno, kParentDirIno); |
+ |
+ std::vector<std::string> dirent_names; |
+ Error error = RequestDirent(dirent_names); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ for (unsigned int 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 GetStat(struct stat* pstat) { |
+ Error error = GetSize(&pstat->st_size); |
+ if (error) { |
+ return error; |
+ } |
+ return 0; |
+ } |
+ |
+ Error Write(const HandleAttr& attr, |
+ const void* buf, |
+ size_t count, |
+ int* out_bytes) { |
+ *out_bytes = 0; |
+ |
+ if (IsaDir()) { |
+ return EISDIR; |
+ } |
+ |
+ off_t file_size = 0; |
+ Error error = GetSize(&file_size); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ off_t file_buf_size = std::max((unsigned long long int)file_size, |
+ (unsigned long long int)attr.offs + count); |
+ char file_buf[file_buf_size]; |
binji
2016/07/18 23:24:02
This will allocate space for the file on the stack
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
Heap memory, as automatically alloc
|
+ |
+ if (file_size > 0) { |
+ int read_helper_out_bytes = 0; |
+ |
+ error = ReadHelper(0, file_size - 1, file_buf, file_size, |
+ &read_helper_out_bytes); |
+ |
+ if (error) { |
+ return error; |
+ } |
+ |
+ if (read_helper_out_bytes != file_size) { |
+ return EPERM; |
+ } |
+ } |
+ |
+ char* pChar = (char*)buf; |
+ for (unsigned int i = 0; i < count; ++i) { |
+ file_buf[attr.offs + i] = pChar[i]; |
+ } |
+ |
+ error = WriteHelper(file_buf, file_buf_size); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ *out_bytes = count; |
+ |
+ return 0; |
+ } |
+ |
+ Error FTruncate(off_t length) { |
+ if (IsaDir()) { |
+ return EISDIR; |
+ } |
+ |
+ off_t file_size = 0; |
+ Error error = GetSize(&file_size); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ char file_buf[length]; |
+ |
+ if (file_size > 0) { |
+ int read_helper_out_bytes = 0; |
+ |
+ error = ReadHelper(0, file_size - 1, file_buf, file_size, |
+ &read_helper_out_bytes); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ if (read_helper_out_bytes != file_size) { |
+ return EPERM; |
+ } |
+ } |
+ |
+ for (int i = file_size; i < length; ++i) { |
+ file_buf[i] = '\0'; |
+ } |
+ |
+ error = WriteHelper(file_buf, length); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ return 0; |
+ } |
+ |
+ Error Read(const HandleAttr& attr, void* buf, size_t count, int* out_bytes) { |
+ *out_bytes = 0; |
+ |
+ if (IsaDir()) { |
+ return EISDIR; |
+ } |
+ |
+ Error error = |
+ ReadHelper(attr.offs, attr.offs + count - 1, buf, count, out_bytes); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ return 0; |
+ } |
+ |
+ int GetType() { return IsaDir() ? S_IFDIR : S_IFREG; } |
+ |
+ Error GetSize(off_t* out_size) { |
+ *out_size = 0; |
+ |
+ if (IsaDir()) { |
+ return 0; |
+ } |
+ |
+ GoogleDriveFs* googledrivefs = dynamic_cast<GoogleDriveFs*>(filesystem_); |
+ if (googledrivefs == NULL) { |
+ return EINVAL; |
+ } |
+ |
+ pp::URLLoader loader(pp::InstanceHandle(googledrivefs->instance())); |
+ pp::URLRequestInfo request(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ std::string url = "https://www.googleapis.com/drive/v3/files/"; |
+ url += item_id_; |
+ url += "?"; |
+ url += "fields=size"; |
+ |
+ bool set_url_ok = request.SetURL(url); |
+ if (!set_url_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_method_ok = request.SetMethod("GET"); |
+ if (!set_method_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::map<std::string, std::string> headers_map; |
+ headers_map["Content-type"] = "application/json"; |
+ std::string authorization_value = "Bearer "; |
+ authorization_value += googledrivefs->token(); |
+ headers_map["Authorization"] = authorization_value; |
+ |
+ std::string headers = MapToHeaders(headers_map); |
+ |
+ bool set_headers_ok = request.SetHeaders(headers); |
+ if (!set_headers_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_allow_cross_origin_requests_ok = |
+ request.SetAllowCrossOriginRequests(true); |
+ if (!set_allow_cross_origin_requests_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_stream_to_file_ok = request.SetStreamToFile(true); |
+ if (!set_stream_to_file_ok) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = loader.Open(request, pp::BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ pp::URLResponseInfo response = loader.GetResponseInfo(); |
+ if (response.GetStatusCode() != 200) { |
+ return EPERM; |
+ } |
+ |
+ pp::FileRef file_ref = response.GetBodyAsFileRef(); |
+ pp::FileIO file_io(pp::InstanceHandle(googledrivefs->instance())); |
+ int32_t open_result = |
+ file_io.Open(file_ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete()); |
+ |
+ if (open_result < 0) { |
+ return PPERROR_TO_ERRNO(open_result); |
+ } |
+ |
+ int BUFFER_SIZE = 1024; |
+ char buffer[BUFFER_SIZE]; |
+ |
+ int32_t byte_read = |
+ file_io.Read(0, buffer, BUFFER_SIZE, pp::BlockUntilComplete()); |
+ while (byte_read == 0) { |
+#ifdef WIN32 |
+ Sleep(1000); |
+#else |
+ usleep(1000000); |
+#endif |
+ |
+ byte_read = |
+ file_io.Read(0, buffer, BUFFER_SIZE, pp::BlockUntilComplete()); |
+ } |
+ if (byte_read < 0) { |
+ return PPERROR_TO_ERRNO(byte_read); |
+ } |
+ |
+ std::string output(buffer); |
+ Json::Value root; |
+ |
+ Json::Reader reader(Json::Features::strictMode()); |
+ if (!reader.parse(output, root, false)) { |
+ return EPERM; |
+ } |
+ |
+ if (!root.isMember("size")) { |
+ return EPERM; |
+ } |
+ |
+ Json::Value size_value = root["size"]; |
+ |
+ *out_size = (off_t)atoi(size_value.asCString()); |
+ |
+ return 0; |
+ } |
+ |
+ Error Init(int open_flags) { |
+ Error error = Node::Init(open_flags); |
+ |
+ if (error) { |
+ return error; |
+ } |
+ |
+ if (path_.Size() == 1) { |
+ parent_dir_id_ = ""; |
+ item_id_ = "root"; |
+ is_dir_item_ = true; |
+ 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 |
+ error = |
+ RequestItemId(parent_dir_id_, path_.Basename(), filesystem_, item_id_); |
+ |
+ if (error == ENOENT) { |
+ // Only files are open as mode O_CREAT |
+ if ((open_flags & O_CREAT) != 0) { |
+ error = CreateEmptyFile(); |
+ if (error) { |
+ return error; |
+ } |
+ error = RequestItemId(parent_dir_id_, path_.Basename(), filesystem_, |
+ item_id_); |
+ if (error) { |
+ return error; |
+ } |
+ is_dir_item_ = false; |
+ } else { |
+ return ENOENT; |
+ } |
+ } else if (error) { |
+ return error; |
+ } else { |
+ std::string dir_id = ""; |
+ error = |
+ RequestDirId(parent_dir_id_, path_.Basename(), filesystem_, dir_id); |
+ if (error == ENOENT) { |
+ is_dir_item_ = false; |
+ } else if (error) { |
+ return error; |
+ } else { |
+ is_dir_item_ = true; |
+ } |
+ |
+ if (open_flags == 0) { |
+ // open_flags == 0 for file opened on fopen with mode r and directory |
+ // opened on opendir |
+ return 0; |
+ } else if (is_dir_item_ && open_flags != 0) { |
+ return EPERM; |
+ } else if ((open_flags & O_TRUNC) != 0) { |
+ error = WriteHelper(NULL, 0); |
+ if (error) { |
+ return error; |
+ } |
+ } |
+ } |
+ |
+ return 0; |
+ } |
+ |
+ void Destroy() { Node::Destroy(); } |
+ |
+ bool IsaDir() { return is_dir_item_; } |
+ |
+ private: |
+ Error ReadHelper(off_t start, |
+ off_t end, |
+ void* buf, |
+ size_t count, |
+ int* out_bytes) { |
+ *out_bytes = 0; |
+ |
+ GoogleDriveFs* googledrivefs = dynamic_cast<GoogleDriveFs*>(filesystem_); |
+ if (googledrivefs == NULL) { |
+ return EINVAL; |
+ } |
+ |
+ pp::URLLoader loader(pp::InstanceHandle(googledrivefs->instance())); |
+ pp::URLRequestInfo request(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ std::string url = "https://www.googleapis.com/download/drive/v3/files/"; |
+ url += item_id_; |
+ url += "?"; |
+ url += "alt=media"; |
+ |
+ bool set_url_ok = request.SetURL(url); |
+ if (!set_url_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_method_ok = request.SetMethod("GET"); |
+ if (!set_method_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::map<std::string, std::string> headers_map; |
+ headers_map["Content-type"] = "application/json"; |
+ std::string authorization_value = "Bearer "; |
+ authorization_value += googledrivefs->token(); |
+ headers_map["Authorization"] = authorization_value; |
+ char range_value_buffer[1024]; |
+ int char_written = sprintf(range_value_buffer, "bytes=%lli-%lli", |
+ (long long int)start, (long long int)end); |
+ if (char_written < 0) { |
+ return EPERM; |
+ } |
+ std::string range_value(range_value_buffer); |
+ headers_map["Range"] = range_value; |
+ |
+ std::string headers = MapToHeaders(headers_map); |
+ |
+ bool set_headers_ok = request.SetHeaders(headers); |
+ if (!set_headers_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_allow_cross_origin_requests_ok = |
+ request.SetAllowCrossOriginRequests(true); |
+ if (!set_allow_cross_origin_requests_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_stream_to_file_ok = request.SetStreamToFile(true); |
+ if (!set_stream_to_file_ok) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = loader.Open(request, pp::BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ pp::URLResponseInfo response = loader.GetResponseInfo(); |
+ |
+ if (response.GetStatusCode() == 200 || response.GetStatusCode() == 206) { |
binji
2016/07/18 23:24:02
use consts for the status codes instead (see http_
chanpatorikku
2016/08/07 02:41:02
Done.
Thanks.
Used consts for the status codes, a
|
+ pp::FileRef file_ref = response.GetBodyAsFileRef(); |
+ pp::FileIO file_io(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ int32_t open_result = file_io.Open(file_ref, PP_FILEOPENFLAG_READ, |
+ pp::BlockUntilComplete()); |
+ if (open_result < 0) { |
+ return PPERROR_TO_ERRNO(open_result); |
+ } |
+ |
+ int32_t byte_read = |
+ file_io.Read(0, (char*)buf, count, pp::BlockUntilComplete()); |
+ while (byte_read == 0) { |
+#ifdef WIN32 |
+ Sleep(1000); |
+#else |
+ usleep(1000000); |
+#endif |
+ |
+ byte_read = |
+ file_io.Read(0, (char*)buf, count, pp::BlockUntilComplete()); |
+ } |
+ |
+ if (byte_read < 0) { |
+ return PPERROR_TO_ERRNO(byte_read); |
+ } |
+ |
+ *out_bytes = byte_read; |
+ |
+ return 0; |
+ |
+ } else if (response.GetStatusCode() == 416) { |
+ *out_bytes = 0; |
+ |
+ char* pChar = (char*)buf; |
+ pChar[0] = '\0'; |
+ |
+ return 0; |
+ } |
+ |
+ return EPERM; |
+ } |
+ |
+ Error WriteHelper(const void* request_body_data, uint32_t request_body_size) { |
+ GoogleDriveFs* googledrivefs = dynamic_cast<GoogleDriveFs*>(filesystem_); |
+ if (googledrivefs == NULL) { |
+ return EINVAL; |
+ } |
+ |
+ pp::URLLoader loader(pp::InstanceHandle(googledrivefs->instance())); |
+ pp::URLRequestInfo request(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ std::string url = "https://www.googleapis.com/upload/drive/v3/files/"; |
+ url += item_id_; |
+ url += "?"; |
+ url += "uploadType=media"; |
+ |
+ bool set_url_ok = request.SetURL(url); |
+ if (!set_url_ok) { |
+ return EPERM; |
+ } |
+ bool set_method_ok = request.SetMethod("PATCH"); |
+ if (!set_method_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::map<std::string, std::string> headers_map; |
+ headers_map["Content-type"] = "application/json"; |
+ std::string authorization_value = "Bearer "; |
+ authorization_value += googledrivefs->token(); |
+ headers_map["Authorization"] = authorization_value; |
+ |
+ std::string headers = MapToHeaders(headers_map); |
+ |
+ bool set_headers_ok = request.SetHeaders(headers); |
+ if (!set_headers_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_allow_cross_origin_requests_ok = |
+ request.SetAllowCrossOriginRequests(true); |
+ if (!set_allow_cross_origin_requests_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_stream_to_file_ok = request.SetStreamToFile(true); |
+ if (!set_stream_to_file_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool append_data_to_body_ok = |
+ request.AppendDataToBody(request_body_data, request_body_size); |
+ if (!append_data_to_body_ok) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = loader.Open(request, pp::BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ pp::URLResponseInfo response = loader.GetResponseInfo(); |
+ if (response.GetStatusCode() != 200) { |
+ return EPERM; |
+ } |
+ |
+ return 0; |
+ } |
+ |
+ Error CreateEmptyFile() { |
+ std::string url = |
+ "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart"; |
+ |
+ GoogleDriveFs* googledrivefs = dynamic_cast<GoogleDriveFs*>(filesystem_); |
+ if (googledrivefs == NULL) { |
+ return EINVAL; |
+ } |
+ |
+ pp::URLLoader loader(pp::InstanceHandle(googledrivefs->instance())); |
+ pp::URLRequestInfo request(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ bool set_url_ok = request.SetURL(url); |
+ if (!set_url_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_method_ok = request.SetMethod("POST"); |
+ if (!set_method_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::string BOUNDARY_VALUE = "foo_bar_baz"; |
+ |
+ std::map<std::string, std::string> headers_map; |
+ headers_map["Content-type"] = |
+ "multipart/related; boundary=" + BOUNDARY_VALUE; |
+ std::string authorization_value = "Bearer "; |
+ authorization_value += googledrivefs->token(); |
+ headers_map["Authorization"] = authorization_value; |
+ |
+ std::string headers = MapToHeaders(headers_map); |
+ |
+ bool set_headers_ok = request.SetHeaders(headers); |
+ if (!set_headers_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_allow_cross_origin_requests_ok = |
+ request.SetAllowCrossOriginRequests(true); |
+ if (!set_allow_cross_origin_requests_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_stream_to_file_ok = request.SetStreamToFile(true); |
+ if (!set_stream_to_file_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::string request_body = "--"; |
+ request_body += BOUNDARY_VALUE; |
+ request_body += "\n"; |
+ |
+ request_body += "Content-Type: application/json; charset=UTF-8"; |
+ request_body += "\n"; |
+ |
+ request_body += "\n"; |
+ |
+ request_body += "{"; |
+ request_body += "\n"; |
+ |
+ request_body += " \"name\": "; |
+ request_body += "\""; |
+ request_body += path_.Basename(); |
+ request_body += "\""; |
+ request_body += ","; |
+ request_body += "\n"; |
+ |
+ request_body += " \"parents\": "; |
+ request_body += "["; |
+ request_body += "\n"; |
+ |
+ request_body += " \""; |
+ request_body += parent_dir_id_; |
+ request_body += "\""; |
+ request_body += "\n"; |
+ |
+ request_body += " ]"; |
+ request_body += "\n"; |
+ |
+ request_body += "}"; |
+ request_body += "\n"; |
+ |
+ request_body += "\n"; |
+ |
+ request_body += "--"; |
+ request_body += BOUNDARY_VALUE; |
+ request_body += "\n"; |
+ |
+ request_body += "Content-Type: text/plain"; |
+ request_body += "\n"; |
+ |
+ request_body += "\n"; |
+ |
+ request_body += "\n"; |
+ |
+ request_body += "--"; |
+ request_body += BOUNDARY_VALUE; |
+ request_body += "--"; |
+ |
+ bool append_data_to_body_ok = |
+ request.AppendDataToBody(request_body.data(), request_body.size()); |
+ if (!append_data_to_body_ok) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = loader.Open(request, pp::BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ pp::URLResponseInfo response = loader.GetResponseInfo(); |
+ if (response.GetStatusCode() != 200) { |
+ return EPERM; |
+ } |
+ |
+ return 0; |
+ } |
+ |
+ Error RequestDirent(std::vector<std::string>& dirent_names) { |
+ std::string url = "https://www.googleapis.com/drive/v3/files"; |
+ url += "?"; |
+ url += "q="; |
+ url += "%27"; |
+ url += item_id_; |
+ url += "%27+in+parents"; |
+ |
+ GoogleDriveFs* googledrivefs = dynamic_cast<GoogleDriveFs*>(filesystem_); |
+ if (googledrivefs == NULL) { |
+ return EINVAL; |
+ } |
+ |
+ pp::URLLoader loader(pp::InstanceHandle(googledrivefs->instance())); |
+ pp::URLRequestInfo request(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ bool set_url_ok = request.SetURL(url); |
+ if (!set_url_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_method_ok = request.SetMethod("GET"); |
+ if (!set_method_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::map<std::string, std::string> headers_map; |
+ headers_map["Content-type"] = "application/json"; |
+ std::string authorization_value = "Bearer "; |
+ authorization_value += googledrivefs->token(); |
+ headers_map["Authorization"] = authorization_value; |
+ |
+ std::string headers = MapToHeaders(headers_map); |
+ |
+ bool set_headers_ok = request.SetHeaders(headers); |
+ if (!set_headers_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_allow_cross_origin_requests_ok = |
+ request.SetAllowCrossOriginRequests(true); |
+ if (!set_allow_cross_origin_requests_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_stream_to_file_ok = request.SetStreamToFile(true); |
+ if (!set_stream_to_file_ok) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = loader.Open(request, pp::BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ pp::URLResponseInfo response = loader.GetResponseInfo(); |
+ if (response.GetStatusCode() != 200) { |
+ return EPERM; |
+ } |
+ |
+ pp::FileRef file_ref = response.GetBodyAsFileRef(); |
+ pp::FileIO file_io(pp::InstanceHandle(googledrivefs->instance())); |
+ |
+ int32_t open_result = |
+ file_io.Open(file_ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete()); |
+ if (open_result < 0) { |
+ return PPERROR_TO_ERRNO(open_result); |
+ } |
+ |
+ std::string output = ""; |
+ int BUFFER_SIZE = 1024; |
+ char buffer[BUFFER_SIZE]; |
+ int byte_read = |
+ file_io.Read(0, buffer, BUFFER_SIZE, pp::BlockUntilComplete()); |
+ |
+ while (byte_read == 0) { |
+#ifdef WIN32 |
+ Sleep(1000); |
+#else |
+ usleep(1000000); |
+#endif |
+ |
+ byte_read = |
+ file_io.Read(0, buffer, BUFFER_SIZE, pp::BlockUntilComplete()); |
+ } |
+ |
+ while (byte_read > 0) { |
+ output += buffer; |
+ |
+ byte_read = file_io.Read(byte_read, buffer, BUFFER_SIZE, |
+ pp::BlockUntilComplete()); |
+ } |
+ |
+ if (byte_read < 0) { |
+ return PPERROR_TO_ERRNO(byte_read); |
+ } |
+ |
+ Json::Value root; |
+ |
+ Json::Reader reader(Json::Features::strictMode()); |
+ if (!reader.parse(output, root, false)) { |
+ return EPERM; |
+ } |
+ |
+ if (!root.isMember("files")) { |
+ return EPERM; |
+ } |
+ |
+ Json::Value files_array = root["files"]; |
+ |
+ if (!files_array.isArray()) { |
+ return EPERM; |
+ } |
+ |
+ for (unsigned int i = 0; i < files_array.size(); ++i) { |
+ dirent_names.push_back(files_array[i]["name"].asString()); |
+ } |
+ |
+ // TODO: Too many entries result in nextPageToken in API response and |
+ // out-of-memory in output variable. |
+ |
+ return 0; |
+ } |
+ |
+ std::string parent_dir_id_; |
+ std::string item_id_; |
+ bool is_dir_item_; |
+ Path path_; |
+ |
+ friend class GoogleDriveFs; |
+}; |
+ |
+GoogleDriveFs::GoogleDriveFs() {} |
+ |
+GoogleDriveFs::~GoogleDriveFs() {} |
+ |
+Error GoogleDriveFs::Init(const FsInitArgs& args) { |
+ Error error = Filesystem::Init(args); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ std::map<std::string, std::string>::const_iterator instance_it = |
+ args.string_map.find("instance"); |
+ |
+ if (instance_it == args.string_map.end()) { |
+ return EINVAL; |
+ } |
+ |
+ instance_ = atoi(instance_it->second.c_str()); |
+ |
+ std::map<std::string, std::string>::const_iterator token_it = |
+ args.string_map.find("token"); |
+ |
+ if (token_it == args.string_map.end()) { |
+ return EINVAL; |
+ } |
+ |
+ token_ = token_it->second; |
+ |
+ return 0; |
+} |
+ |
+void GoogleDriveFs::Destroy() {} |
+ |
+Error GoogleDriveFs::OpenWithMode(const Path& path, |
+ int open_flags, |
+ mode_t mode, |
+ ScopedNode* out_node) { |
+ ScopedNode node(new GoogleDriveFsNode(this, path)); |
+ |
+ Error error = node->Init(open_flags); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ *out_node = node; |
+ |
+ return 0; |
+} |
+ |
+Error GoogleDriveFs::Rename(const Path& path, const Path& newPath) { |
+ LOG_ERROR("rename not supported."); |
+ return EPERM; |
+} |
+ |
+Error GoogleDriveFs::Unlink(const Path& path) { |
+ LOG_ERROR("unlink not supported."); |
+ return EPERM; |
+} |
+ |
+Error GoogleDriveFs::Mkdir(const Path& path, int permissions) { |
+ std::string parent_dir_id = ""; |
+ Error error = RequestParentDirId(path, this, parent_dir_id); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ std::string item_id = ""; |
+ error = RequestItemId(parent_dir_id, path.Basename(), this, item_id); |
+ |
+ // mkdir does not create a directory when a file with path.Basename() |
+ // already exists |
+ if (error == ENOENT) { |
+ std::string url = "https://www.googleapis.com/drive/v3/files"; |
+ |
+ pp::URLLoader loader(pp::InstanceHandle(this->instance())); |
+ pp::URLRequestInfo request(pp::InstanceHandle(this->instance())); |
+ |
+ bool set_url_ok = request.SetURL(url); |
+ if (!set_url_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_method_ok = request.SetMethod("POST"); |
+ if (!set_method_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::map<std::string, std::string> headers_map; |
+ headers_map["Content-type"] = "application/json"; |
+ std::string authorization_value = "Bearer "; |
+ authorization_value += token_; |
+ headers_map["Authorization"] = authorization_value; |
+ |
+ std::string headers = MapToHeaders(headers_map); |
+ |
+ bool set_headers_ok = request.SetHeaders(headers); |
+ if (!set_headers_ok) { |
+ return EPERM; |
+ } |
+ |
+ Json::Value json_value; |
+ json_value["name"] = path.Basename(); |
+ json_value["mimeType"] = "application/vnd.google-apps.folder"; |
+ Json::FastWriter fast_writer; |
+ std::string request_body = fast_writer.write(json_value); |
+ |
+ bool append_data_to_body_ok = |
+ request.AppendDataToBody(request_body.data(), request_body.size()); |
+ if (!append_data_to_body_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_allow_cross_origin_requests_ok = |
+ request.SetAllowCrossOriginRequests(true); |
+ if (!set_allow_cross_origin_requests_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_stream_to_file_ok = request.SetStreamToFile(true); |
+ if (!set_stream_to_file_ok) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = loader.Open(request, pp::BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ pp::URLResponseInfo response = loader.GetResponseInfo(); |
+ if (response.GetStatusCode() != 200) { |
+ return EPERM; |
+ } |
+ |
+ return 0; |
+ } |
+ |
+ return EEXIST; |
+} |
+ |
+Error GoogleDriveFs::Rmdir(const Path& path) { |
+ std::string parent_dir_id = ""; |
+ Error error = RequestParentDirId(path, this, parent_dir_id); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ std::string dir_id = ""; |
+ error = RequestDirId(parent_dir_id, path.Basename(), this, dir_id); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ std::string url = "https://www.googleapis.com/drive/v3/files"; |
+ |
+ url += "/"; |
+ url += dir_id; |
+ |
+ pp::URLLoader loader(pp::InstanceHandle(this->instance())); |
+ pp::URLRequestInfo request(pp::InstanceHandle(this->instance())); |
+ |
+ bool set_url_ok = request.SetURL(url); |
+ if (!set_url_ok) { |
+ return EPERM; |
+ } |
+ bool set_method_ok = request.SetMethod("DELETE"); |
+ if (!set_method_ok) { |
+ return EPERM; |
+ } |
+ |
+ std::map<std::string, std::string> headers_map; |
+ headers_map["Content-type"] = "application/json"; |
+ std::string authorization_value = "Bearer "; |
+ authorization_value += token_; |
+ headers_map["Authorization"] = authorization_value; |
+ |
+ std::string headers = MapToHeaders(headers_map); |
+ |
+ bool set_headers_ok = request.SetHeaders(headers); |
+ if (!set_headers_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_allow_cross_origin_requests_ok = |
+ request.SetAllowCrossOriginRequests(true); |
+ if (!set_allow_cross_origin_requests_ok) { |
+ return EPERM; |
+ } |
+ |
+ bool set_stream_to_file_ok = request.SetStreamToFile(true); |
+ if (!set_stream_to_file_ok) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = loader.Open(request, pp::BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ pp::URLResponseInfo response = loader.GetResponseInfo(); |
+ if (response.GetStatusCode() != 204) { |
+ return EPERM; |
+ } |
+ |
+ return 0; |
+} |
+ |
+Error GoogleDriveFs::Remove(const Path& path) { |
+ LOG_ERROR("remove not supported."); |
+ return EPERM; |
+} |
+ |
+std::string GoogleDriveFs::token() { |
+ return token_; |
+} |
+ |
+PP_Instance GoogleDriveFs::instance() { |
+ return instance_; |
+} |
+ |
+} // namespace nacl_io |