• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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/httpfs/http_fs.h"
6 
7 #include <assert.h>
8 #include <ctype.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 
16 #include <vector>
17 
18 #include <ppapi/c/pp_errors.h>
19 
20 #include "nacl_io/dir_node.h"
21 #include "nacl_io/httpfs/http_fs_node.h"
22 #include "nacl_io/kernel_handle.h"
23 #include "nacl_io/log.h"
24 #include "nacl_io/osinttypes.h"
25 #include "nacl_io/osunistd.h"
26 #include "sdk_util/string_util.h"
27 
28 namespace nacl_io {
29 
NormalizeHeaderKey(const std::string & s)30 std::string NormalizeHeaderKey(const std::string& s) {
31   // Capitalize the first letter and any letter following a hyphen:
32   // e.g. ACCEPT-ENCODING -> Accept-Encoding
33   std::string result;
34   bool upper = true;
35   for (size_t i = 0; i < s.length(); ++i) {
36     char c = s[i];
37     result += upper ? toupper(c) : tolower(c);
38     upper = c == '-';
39   }
40 
41   return result;
42 }
43 
Access(const Path & path,int a_mode)44 Error HttpFs::Access(const Path& path, int a_mode) {
45   assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/');
46 
47   NodeMap_t::iterator iter = node_cache_.find(path.Join());
48   if (iter == node_cache_.end()) {
49     // If we can't find the node in the cache, fetch it
50     std::string url = MakeUrl(path);
51     ScopedNode node(new HttpFsNode(this, url, cache_content_));
52     Error error = node->Init(0);
53     if (error)
54       return error;
55 
56     error = node->GetStat(NULL);
57     if (error)
58       return error;
59   }
60 
61   // Don't allow write or execute access.
62   if (a_mode & (W_OK | X_OK))
63     return EACCES;
64 
65   return 0;
66 }
67 
Open(const Path & path,int open_flags,ScopedNode * out_node)68 Error HttpFs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
69   out_node->reset(NULL);
70   assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/');
71 
72   NodeMap_t::iterator iter = node_cache_.find(path.Join());
73   if (iter != node_cache_.end()) {
74     *out_node = iter->second;
75     return 0;
76   }
77 
78   // If we can't find the node in the cache, create it
79   std::string url = MakeUrl(path);
80   ScopedNode node(new HttpFsNode(this, url, cache_content_));
81   Error error = node->Init(open_flags);
82   if (error)
83     return error;
84 
85   error = node->GetStat(NULL);
86   if (error)
87     return error;
88 
89   ScopedNode parent;
90   error = FindOrCreateDir(path.Parent(), &parent);
91   if (error)
92     return error;
93 
94   error = parent->AddChild(path.Basename(), node);
95   if (error)
96     return error;
97 
98   node_cache_[path.Join()] = node;
99   *out_node = node;
100   return 0;
101 }
102 
Unlink(const Path & path)103 Error HttpFs::Unlink(const Path& path) {
104   NodeMap_t::iterator iter = node_cache_.find(path.Join());
105   if (iter == node_cache_.end())
106     return ENOENT;
107 
108   if (iter->second->IsaDir())
109     return EISDIR;
110 
111   return EACCES;
112 }
113 
Mkdir(const Path & path,int permissions)114 Error HttpFs::Mkdir(const Path& path, int permissions) {
115   NodeMap_t::iterator iter = node_cache_.find(path.Join());
116   if (iter != node_cache_.end()) {
117     if (iter->second->IsaDir())
118       return EEXIST;
119   }
120   return EACCES;
121 }
122 
Rmdir(const Path & path)123 Error HttpFs::Rmdir(const Path& path) {
124   NodeMap_t::iterator iter = node_cache_.find(path.Join());
125   if (iter == node_cache_.end())
126     return ENOENT;
127 
128   if (!iter->second->IsaDir())
129     return ENOTDIR;
130 
131   return EACCES;
132 }
133 
Remove(const Path & path)134 Error HttpFs::Remove(const Path& path) {
135   NodeMap_t::iterator iter = node_cache_.find(path.Join());
136   if (iter == node_cache_.end())
137     return ENOENT;
138 
139   return EACCES;
140 }
141 
Rename(const Path & path,const Path & newpath)142 Error HttpFs::Rename(const Path& path, const Path& newpath) {
143   NodeMap_t::iterator iter = node_cache_.find(path.Join());
144   if (iter == node_cache_.end())
145     return ENOENT;
146 
147   return EACCES;
148 }
149 
MakeUrlRequestInfo(const std::string & url,const char * method,StringMap_t * additional_headers)150 PP_Resource HttpFs::MakeUrlRequestInfo(const std::string& url,
151                                        const char* method,
152                                        StringMap_t* additional_headers) {
153   URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface();
154   VarInterface* var_interface = ppapi_->GetVarInterface();
155 
156   PP_Resource request_info = interface->Create(ppapi_->GetInstance());
157   if (!request_info)
158     return 0;
159 
160   interface->SetProperty(request_info,
161                          PP_URLREQUESTPROPERTY_URL,
162                          var_interface->VarFromUtf8(url.c_str(), url.length()));
163   interface->SetProperty(request_info,
164                          PP_URLREQUESTPROPERTY_METHOD,
165                          var_interface->VarFromUtf8(method, strlen(method)));
166   interface->SetProperty(request_info,
167                          PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS,
168                          PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE));
169   interface->SetProperty(request_info,
170                          PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS,
171                          PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE));
172 
173   // Merge the filesystem headers with the request headers. If the field is
174   // already set it |additional_headers|, don't use the one from headers_.
175   for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end();
176        ++iter) {
177     const std::string& key = NormalizeHeaderKey(iter->first);
178     if (additional_headers->find(key) == additional_headers->end()) {
179       additional_headers->insert(std::make_pair(key, iter->second));
180     }
181   }
182 
183   // Join the headers into one string.
184   std::string headers;
185   for (StringMap_t::iterator iter = additional_headers->begin();
186        iter != additional_headers->end();
187        ++iter) {
188     headers += iter->first + ": " + iter->second + '\n';
189   }
190 
191   interface->SetProperty(
192       request_info,
193       PP_URLREQUESTPROPERTY_HEADERS,
194       var_interface->VarFromUtf8(headers.c_str(), headers.length()));
195 
196   return request_info;
197 }
198 
HttpFs()199 HttpFs::HttpFs()
200     : allow_cors_(false),
201       allow_credentials_(false),
202       cache_stat_(true),
203       cache_content_(true) {
204 }
205 
Init(const FsInitArgs & args)206 Error HttpFs::Init(const FsInitArgs& args) {
207   Error error = Filesystem::Init(args);
208   if (error)
209     return error;
210 
211   // Parse filesystem args.
212   for (StringMap_t::const_iterator iter = args.string_map.begin();
213        iter != args.string_map.end();
214        ++iter) {
215     if (iter->first == "SOURCE") {
216       url_root_ = iter->second;
217 
218       // Make sure url_root_ ends with a slash.
219       if (!url_root_.empty() && url_root_[url_root_.length() - 1] != '/') {
220         url_root_ += '/';
221       }
222     } else if (iter->first == "manifest") {
223       char* text;
224       error = LoadManifest(iter->second, &text);
225       if (error)
226         return error;
227 
228       error = ParseManifest(text);
229       if (error) {
230         delete[] text;
231         return error;
232       }
233 
234       delete[] text;
235     } else if (iter->first == "allow_cross_origin_requests") {
236       allow_cors_ = iter->second == "true";
237     } else if (iter->first == "allow_credentials") {
238       allow_credentials_ = iter->second == "true";
239     } else if (iter->first == "cache_stat") {
240       cache_stat_ = iter->second == "true";
241     } else if (iter->first == "cache_content") {
242       cache_content_ = iter->second == "true";
243     } else {
244       // Assume it is a header to pass to an HTTP request.
245       headers_[NormalizeHeaderKey(iter->first)] = iter->second;
246     }
247   }
248 
249   return 0;
250 }
251 
Destroy()252 void HttpFs::Destroy() {
253 }
254 
FindOrCreateDir(const Path & path,ScopedNode * out_node)255 Error HttpFs::FindOrCreateDir(const Path& path, ScopedNode* out_node) {
256   out_node->reset(NULL);
257   std::string strpath = path.Join();
258   NodeMap_t::iterator iter = node_cache_.find(strpath);
259   if (iter != node_cache_.end()) {
260     *out_node = iter->second;
261     return 0;
262   }
263 
264   // If the node does not exist, create it.
265   ScopedNode node(new DirNode(this));
266   Error error = node->Init(0);
267   if (error)
268     return error;
269 
270   // If not the root node, find the parent node and add it to the parent
271   if (!path.IsRoot()) {
272     ScopedNode parent;
273     error = FindOrCreateDir(path.Parent(), &parent);
274     if (error)
275       return error;
276 
277     error = parent->AddChild(path.Basename(), node);
278     if (error)
279       return error;
280   }
281 
282   // Add it to the node cache.
283   node_cache_[strpath] = node;
284   *out_node = node;
285   return 0;
286 }
287 
ParseManifest(const char * text)288 Error HttpFs::ParseManifest(const char* text) {
289   std::vector<std::string> lines;
290   sdk_util::SplitString(text, '\n', &lines);
291 
292   for (size_t i = 0; i < lines.size(); i++) {
293     std::vector<std::string> words;
294     sdk_util::SplitString(lines[i], ' ', &words);
295 
296     // Remove empty words (due to multiple consecutive spaces).
297     std::vector<std::string> non_empty_words;
298     for (std::vector<std::string>::const_iterator it = words.begin();
299          it != words.end();
300          ++it) {
301       if (!it->empty())
302         non_empty_words.push_back(*it);
303     }
304 
305     if (non_empty_words.size() == 3) {
306       const std::string& modestr = non_empty_words[0];
307       const std::string& lenstr = non_empty_words[1];
308       const std::string& name = non_empty_words[2];
309 
310       assert(modestr.size() == 4);
311       assert(name[0] == '/');
312 
313       // Only support regular and streams for now
314       // Ignore EXEC bit
315       int mode = S_IFREG;
316       switch (modestr[0]) {
317         case '-':
318           mode = S_IFREG;
319           break;
320         case 'c':
321           mode = S_IFCHR;
322           break;
323         default:
324           LOG_ERROR("Unable to parse type %s for %s.",
325                     modestr.c_str(),
326                     name.c_str());
327           return EINVAL;
328       }
329 
330       switch (modestr[1]) {
331         case '-':
332           break;
333         case 'r':
334           mode |= S_IRUSR | S_IRGRP | S_IROTH;
335           break;
336         default:
337           LOG_ERROR("Unable to parse read %s for %s.",
338                     modestr.c_str(),
339                     name.c_str());
340           return EINVAL;
341       }
342 
343       switch (modestr[2]) {
344         case '-':
345           break;
346         case 'w':
347           mode |= S_IWUSR | S_IWGRP | S_IWOTH;
348           break;
349         default:
350           LOG_ERROR("Unable to parse write %s for %s.",
351                     modestr.c_str(),
352                     name.c_str());
353           return EINVAL;
354       }
355 
356       Path path(name);
357       std::string url = MakeUrl(path);
358 
359       HttpFsNode* http_node = new HttpFsNode(this, url, cache_content_);
360       http_node->SetMode(mode);
361       ScopedNode node(http_node);
362 
363       Error error = node->Init(0);
364       if (error)
365         return error;
366       http_node->SetCachedSize(atoi(lenstr.c_str()));
367 
368       ScopedNode dir_node;
369       error = FindOrCreateDir(path.Parent(), &dir_node);
370       if (error)
371         return error;
372 
373       error = dir_node->AddChild(path.Basename(), node);
374       if (error)
375         return error;
376 
377       std::string pname = path.Join();
378       node_cache_[pname] = node;
379     }
380   }
381 
382   return 0;
383 }
384 
LoadManifest(const std::string & manifest_name,char ** out_manifest)385 Error HttpFs::LoadManifest(const std::string& manifest_name,
386                            char** out_manifest) {
387   Path manifest_path(manifest_name);
388   ScopedNode manifest_node;
389   *out_manifest = NULL;
390 
391   int error = Open(manifest_path, O_RDONLY, &manifest_node);
392   if (error)
393     return error;
394 
395   off_t size;
396   error = manifest_node->GetSize(&size);
397   if (error)
398     return error;
399 
400   char* text = new char[size + 1];
401   int len;
402   error = manifest_node->Read(HandleAttr(), text, size, &len);
403   if (error)
404     return error;
405 
406   text[len] = 0;
407   *out_manifest = text;
408   return 0;
409 }
410 
MakeUrl(const Path & path)411 std::string HttpFs::MakeUrl(const Path& path) {
412   return url_root_ +
413          (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join());
414 }
415 
416 }  // namespace nacl_io
417