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