OLD | NEW |
| (Empty) |
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 | |
6 #include "nacl_mounts/mount_http.h" | |
7 #include <assert.h> | |
8 #include <ctype.h> | |
9 #include <errno.h> | |
10 #include <fcntl.h> | |
11 #include <ppapi/c/pp_errors.h> | |
12 #include <stdio.h> | |
13 #include <string.h> | |
14 #include <sys/stat.h> | |
15 #include <sys/types.h> | |
16 #include <vector> | |
17 #include "nacl_mounts/mount_node_dir.h" | |
18 #include "nacl_mounts/osinttypes.h" | |
19 #include "utils/auto_lock.h" | |
20 | |
21 #if defined(WIN32) | |
22 #define snprintf _snprintf | |
23 #endif | |
24 | |
25 | |
26 typedef std::vector<char *> StringList_t; | |
27 static size_t SplitString(char *str, const char *delim, StringList_t* list) { | |
28 char *item = strtok(str, delim); | |
29 | |
30 list->clear(); | |
31 while (item) { | |
32 list->push_back(item); | |
33 item = strtok(NULL, delim); | |
34 } | |
35 | |
36 return list->size(); | |
37 } | |
38 | |
39 | |
40 // If we're attempting to read a partial request, but the server returns a full | |
41 // request, we need to read all of the data up to the start of our partial | |
42 // request into a dummy buffer. This is the maximum size of that buffer. | |
43 static const size_t MAX_READ_BUFFER_SIZE = 64 * 1024; | |
44 static const int32_t STATUSCODE_OK = 200; | |
45 static const int32_t STATUSCODE_PARTIAL_CONTENT = 206; | |
46 | |
47 std::string NormalizeHeaderKey(const std::string& s) { | |
48 // Capitalize the first letter and any letter following a hyphen: | |
49 // e.g. ACCEPT-ENCODING -> Accept-Encoding | |
50 std::string result; | |
51 bool upper = true; | |
52 for (size_t i = 0; i < s.length(); ++i) { | |
53 char c = s[i]; | |
54 result += upper ? toupper(c) : tolower(c); | |
55 upper = c == '-'; | |
56 } | |
57 | |
58 return result; | |
59 } | |
60 | |
61 StringMap_t ParseHeaders(const char* headers, int32_t headers_length) { | |
62 enum State { | |
63 FINDING_KEY, | |
64 SKIPPING_WHITESPACE, | |
65 FINDING_VALUE, | |
66 }; | |
67 | |
68 StringMap_t result; | |
69 std::string key; | |
70 std::string value; | |
71 | |
72 State state = FINDING_KEY; | |
73 const char* start = headers; | |
74 for (int i = 0; i < headers_length; ++i) { | |
75 switch (state) { | |
76 case FINDING_KEY: | |
77 if (headers[i] == ':') { | |
78 // Found key. | |
79 key.assign(start, &headers[i] - start); | |
80 key = NormalizeHeaderKey(key); | |
81 state = SKIPPING_WHITESPACE; | |
82 } | |
83 break; | |
84 | |
85 case SKIPPING_WHITESPACE: | |
86 if (headers[i] == ' ') { | |
87 // Found whitespace, keep going... | |
88 break; | |
89 } | |
90 | |
91 // Found a non-whitespace, mark this as the start of the value. | |
92 start = &headers[i]; | |
93 state = FINDING_VALUE; | |
94 // Fallthrough to start processing value without incrementing i. | |
95 | |
96 case FINDING_VALUE: | |
97 if (headers[i] == '\n') { | |
98 // Found value. | |
99 value.assign(start, &headers[i] - start); | |
100 result[key] = value; | |
101 start = &headers[i + 1]; | |
102 state = FINDING_KEY; | |
103 } | |
104 break; | |
105 } | |
106 } | |
107 | |
108 return result; | |
109 } | |
110 | |
111 bool ParseContentLength(const StringMap_t& headers, size_t* content_length) { | |
112 StringMap_t::const_iterator iter = headers.find("Content-Length"); | |
113 if (iter == headers.end()) | |
114 return false; | |
115 | |
116 *content_length = strtoul(iter->second.c_str(), NULL, 10); | |
117 return true; | |
118 } | |
119 | |
120 bool ParseContentRange(const StringMap_t& headers, size_t* read_start, | |
121 size_t* read_end, size_t* entity_length) { | |
122 StringMap_t::const_iterator iter = headers.find("Content-Range"); | |
123 if (iter == headers.end()) | |
124 return false; | |
125 | |
126 // The key should look like "bytes ##-##/##" or "bytes ##-##/*". The last | |
127 // value is the entity length, which can potentially be * (i.e. unknown). | |
128 int read_start_int; | |
129 int read_end_int; | |
130 int entity_length_int; | |
131 int result = sscanf(iter->second.c_str(), "bytes %"SCNuS"-%"SCNuS"/%"SCNuS, | |
132 &read_start_int, &read_end_int, &entity_length_int); | |
133 | |
134 // The Content-Range header specifies an inclusive range: e.g. the first ten | |
135 // bytes is "bytes 0-9/*". Convert it to a half-open range by incrementing | |
136 // read_end. | |
137 if (result == 2) { | |
138 *read_start = read_start_int; | |
139 *read_end = read_end_int + 1; | |
140 *entity_length = 0; | |
141 return true; | |
142 } else if (result == 3) { | |
143 *read_start = read_start_int; | |
144 *read_end = read_end_int + 1; | |
145 *entity_length = entity_length_int; | |
146 return true; | |
147 } | |
148 | |
149 return false; | |
150 } | |
151 | |
152 class MountNodeHttp : public MountNode { | |
153 public: | |
154 virtual int FSync(); | |
155 virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); | |
156 virtual int GetStat(struct stat* stat); | |
157 virtual int Read(size_t offs, void* buf, size_t count); | |
158 virtual int Truncate(size_t size); | |
159 virtual int Write(size_t offs, const void* buf, size_t count); | |
160 virtual size_t GetSize(); | |
161 | |
162 protected: | |
163 MountNodeHttp(Mount* mount, const std::string& url); | |
164 | |
165 private: | |
166 bool OpenUrl(const char* method, | |
167 StringMap_t* request_headers, | |
168 PP_Resource* out_loader, | |
169 PP_Resource* out_request, | |
170 PP_Resource* out_response, | |
171 int32_t* out_statuscode, | |
172 StringMap_t* out_response_headers); | |
173 | |
174 std::string url_; | |
175 std::vector<char> buffer_; | |
176 friend class ::MountHttp; | |
177 }; | |
178 | |
179 int MountNodeHttp::FSync() { | |
180 errno = ENOSYS; | |
181 return -1; | |
182 } | |
183 | |
184 int MountNodeHttp::GetDents(size_t offs, struct dirent* pdir, size_t count) { | |
185 errno = ENOSYS; | |
186 return -1; | |
187 } | |
188 | |
189 int MountNodeHttp::GetStat(struct stat* stat) { | |
190 AutoLock lock(&lock_); | |
191 | |
192 // Assume we need to 'HEAD' if we do not know the size, otherwise, assume | |
193 // that the information is constant. We can add a timeout if needed. | |
194 MountHttp* mount = static_cast<MountHttp*>(mount_); | |
195 if (stat_.st_size == 0 || !mount->allow_stat_cache_) { | |
196 StringMap_t headers; | |
197 PP_Resource loader; | |
198 PP_Resource request; | |
199 PP_Resource response; | |
200 int32_t statuscode; | |
201 StringMap_t response_headers; | |
202 if (!OpenUrl("HEAD", &headers, &loader, &request, &response, &statuscode, | |
203 &response_headers)) { | |
204 // errno is already set by OpenUrl. | |
205 return -1; | |
206 } | |
207 | |
208 ScopedResource scoped_loader(mount_->ppapi(), loader); | |
209 ScopedResource scoped_request(mount_->ppapi(), request); | |
210 ScopedResource scoped_response(mount_->ppapi(), response); | |
211 | |
212 | |
213 size_t entity_length; | |
214 if (ParseContentLength(response_headers, &entity_length)) | |
215 stat_.st_size = static_cast<off_t>(entity_length); | |
216 else | |
217 stat_.st_size = 0; | |
218 | |
219 stat_.st_atime = 0; // TODO(binji): Use "Last-Modified". | |
220 stat_.st_mtime = 0; | |
221 stat_.st_ctime = 0; | |
222 } | |
223 | |
224 // Fill the stat structure if provided | |
225 if (stat) { | |
226 memcpy(stat, &stat_, sizeof(stat_)); | |
227 } | |
228 return 0; | |
229 } | |
230 | |
231 int MountNodeHttp::Read(size_t offs, void* buf, size_t count) { | |
232 AutoLock lock(&lock_); | |
233 StringMap_t headers; | |
234 | |
235 char buffer[100]; | |
236 // Range request is inclusive: 0-99 returns 100 bytes. | |
237 snprintf(&buffer[0], sizeof(buffer), "bytes=%"PRIuS"-%"PRIuS, | |
238 offs, offs + count - 1); | |
239 headers["Range"] = buffer; | |
240 | |
241 PP_Resource loader; | |
242 PP_Resource request; | |
243 PP_Resource response; | |
244 int32_t statuscode; | |
245 StringMap_t response_headers; | |
246 if (!OpenUrl("GET", &headers, &loader, &request, &response, &statuscode, | |
247 &response_headers)) { | |
248 // errno is already set by OpenUrl. | |
249 return 0; | |
250 } | |
251 | |
252 PepperInterface* ppapi = mount_->ppapi(); | |
253 ScopedResource scoped_loader(ppapi, loader); | |
254 ScopedResource scoped_request(ppapi, request); | |
255 ScopedResource scoped_response(ppapi, response); | |
256 | |
257 size_t read_start = 0; | |
258 if (statuscode == STATUSCODE_OK) { | |
259 // No partial result, read everything starting from the part we care about. | |
260 size_t content_length; | |
261 if (ParseContentLength(response_headers, &content_length)) { | |
262 if (offs >= content_length) { | |
263 errno = EINVAL; | |
264 return 0; | |
265 } | |
266 | |
267 // Clamp count, if trying to read past the end of the file. | |
268 if (offs + count > content_length) { | |
269 count = content_length - offs; | |
270 } | |
271 } | |
272 } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) { | |
273 // Determine from the headers where we are reading. | |
274 size_t read_end; | |
275 size_t entity_length; | |
276 if (ParseContentRange(response_headers, &read_start, &read_end, | |
277 &entity_length)) { | |
278 if (read_start > offs || read_start > read_end) { | |
279 // Shouldn't happen. | |
280 errno = EINVAL; | |
281 return 0; | |
282 } | |
283 | |
284 // Clamp count, if trying to read past the end of the file. | |
285 count = std::min(read_end - read_start, count); | |
286 } else { | |
287 // Partial Content without Content-Range. Assume that the server gave us | |
288 // exactly what we asked for. This can happen even when the server | |
289 // returns 200 -- the cache may return 206 in this case, but not modify | |
290 // the headers. | |
291 read_start = offs; | |
292 } | |
293 } | |
294 | |
295 URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); | |
296 | |
297 size_t bytes_to_read; | |
298 int32_t bytes_read; | |
299 while (read_start < offs) { | |
300 if (!buffer_.size()) { | |
301 buffer_.resize(std::min(offs - read_start, MAX_READ_BUFFER_SIZE)); | |
302 } | |
303 | |
304 // We aren't yet at the location where we want to start reading. Read into | |
305 // our dummy buffer until then. | |
306 bytes_to_read = std::min(offs - read_start, buffer_.size()); | |
307 bytes_read = loader_interface->ReadResponseBody( | |
308 loader, buffer_.data(), bytes_to_read, PP_BlockUntilComplete()); | |
309 | |
310 if (bytes_read < 0) { | |
311 errno = PPErrorToErrno(bytes_read); | |
312 return 0; | |
313 } | |
314 | |
315 assert(bytes_read <= bytes_to_read); | |
316 read_start += bytes_read; | |
317 } | |
318 | |
319 // At the read start, now we can read into the correct buffer. | |
320 char* out_buffer = static_cast<char*>(buf); | |
321 bytes_to_read = count; | |
322 while (bytes_to_read > 0) { | |
323 bytes_read = loader_interface->ReadResponseBody( | |
324 loader, out_buffer, bytes_to_read, PP_BlockUntilComplete()); | |
325 | |
326 if (bytes_read == 0) { | |
327 // This is not an error -- it may just be that we were trying to read | |
328 // more data than exists. | |
329 return count - bytes_to_read; | |
330 } | |
331 | |
332 if (bytes_read < 0) { | |
333 errno = PPErrorToErrno(bytes_read); | |
334 return count - bytes_to_read; | |
335 } | |
336 | |
337 assert(bytes_read <= bytes_to_read); | |
338 bytes_to_read -= bytes_read; | |
339 out_buffer += bytes_read; | |
340 } | |
341 | |
342 return count; | |
343 } | |
344 | |
345 int MountNodeHttp::Truncate(size_t size) { | |
346 errno = ENOSYS; | |
347 return -1; | |
348 } | |
349 | |
350 int MountNodeHttp::Write(size_t offs, const void* buf, size_t count) { | |
351 // TODO(binji): support POST? | |
352 errno = ENOSYS; | |
353 return -1; | |
354 } | |
355 | |
356 size_t MountNodeHttp::GetSize() { | |
357 // TODO(binji): This value should be cached properly; i.e. obey the caching | |
358 // headers returned by the server. | |
359 return stat_.st_size; | |
360 } | |
361 | |
362 MountNodeHttp::MountNodeHttp(Mount* mount, const std::string& url) | |
363 : MountNode(mount), | |
364 url_(url) { | |
365 } | |
366 | |
367 bool MountNodeHttp::OpenUrl(const char* method, | |
368 StringMap_t* request_headers, | |
369 PP_Resource* out_loader, | |
370 PP_Resource* out_request, | |
371 PP_Resource* out_response, | |
372 int32_t* out_statuscode, | |
373 StringMap_t* out_response_headers) { | |
374 // Assume lock_ is already held. | |
375 | |
376 PepperInterface* ppapi = mount_->ppapi(); | |
377 | |
378 MountHttp* mount_http = static_cast<MountHttp*>(mount_); | |
379 ScopedResource request(ppapi, | |
380 mount_http->MakeUrlRequestInfo(url_, method, | |
381 request_headers)); | |
382 if (!request.pp_resource()) { | |
383 errno = EINVAL; | |
384 return false; | |
385 } | |
386 | |
387 URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); | |
388 URLResponseInfoInterface* response_interface = | |
389 ppapi->GetURLResponseInfoInterface(); | |
390 VarInterface* var_interface = ppapi->GetVarInterface(); | |
391 | |
392 ScopedResource loader(ppapi, loader_interface->Create(ppapi->GetInstance())); | |
393 if (!loader.pp_resource()) { | |
394 errno = EINVAL; | |
395 return false; | |
396 } | |
397 | |
398 int32_t result = loader_interface->Open( | |
399 loader.pp_resource(), request.pp_resource(), PP_BlockUntilComplete()); | |
400 if (result != PP_OK) { | |
401 errno = PPErrorToErrno(result); | |
402 return false; | |
403 } | |
404 | |
405 ScopedResource response( | |
406 ppapi, | |
407 loader_interface->GetResponseInfo(loader.pp_resource())); | |
408 if (!response.pp_resource()) { | |
409 errno = EINVAL; | |
410 return false; | |
411 } | |
412 | |
413 // Get response statuscode. | |
414 PP_Var statuscode = response_interface->GetProperty( | |
415 response.pp_resource(), | |
416 PP_URLRESPONSEPROPERTY_STATUSCODE); | |
417 | |
418 if (statuscode.type != PP_VARTYPE_INT32) { | |
419 errno = EINVAL; | |
420 return false; | |
421 } | |
422 | |
423 *out_statuscode = statuscode.value.as_int; | |
424 | |
425 // Only accept OK or Partial Content. | |
426 if (*out_statuscode != STATUSCODE_OK && | |
427 *out_statuscode != STATUSCODE_PARTIAL_CONTENT) { | |
428 errno = EINVAL; | |
429 return false; | |
430 } | |
431 | |
432 // Get response headers. | |
433 PP_Var response_headers_var = response_interface->GetProperty( | |
434 response.pp_resource(), | |
435 PP_URLRESPONSEPROPERTY_HEADERS); | |
436 | |
437 uint32_t response_headers_length; | |
438 const char* response_headers_str = var_interface->VarToUtf8( | |
439 response_headers_var, | |
440 &response_headers_length); | |
441 | |
442 *out_loader = loader.Release(); | |
443 *out_request = request.Release(); | |
444 *out_response = response.Release(); | |
445 *out_response_headers = ParseHeaders(response_headers_str, | |
446 response_headers_length); | |
447 | |
448 return true; | |
449 } | |
450 | |
451 MountNode *MountHttp::Open(const Path& path, int mode) { | |
452 assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); | |
453 | |
454 NodeMap_t::iterator iter = node_cache_.find(path.Join()); | |
455 if (iter != node_cache_.end()) { | |
456 return iter->second; | |
457 } | |
458 | |
459 // If we can't find the node in the cache, create it | |
460 std::string url = url_root_ + (path.IsAbsolute() ? | |
461 path.Range(1, path.Size()) : | |
462 path.Join()); | |
463 | |
464 MountNodeHttp* node = new MountNodeHttp(this, url); | |
465 if (!node->Init(mode) || (0 != node->GetStat(NULL))) { | |
466 node->Release(); | |
467 return NULL; | |
468 } | |
469 | |
470 MountNodeDir* parent = FindOrCreateDir(path.Parent()); | |
471 node_cache_[path.Join()] = node; | |
472 parent->AddChild(path.Basename(), node); | |
473 return node; | |
474 } | |
475 | |
476 int MountHttp::Unlink(const Path& path) { | |
477 errno = ENOSYS; | |
478 return -1; | |
479 } | |
480 | |
481 int MountHttp::Mkdir(const Path& path, int permissions) { | |
482 errno = ENOSYS; | |
483 return -1; | |
484 } | |
485 | |
486 int MountHttp::Rmdir(const Path& path) { | |
487 errno = ENOSYS; | |
488 return -1; | |
489 } | |
490 | |
491 int MountHttp::Remove(const Path& path) { | |
492 errno = ENOSYS; | |
493 return -1; | |
494 } | |
495 | |
496 PP_Resource MountHttp::MakeUrlRequestInfo( | |
497 const std::string& url, | |
498 const char* method, | |
499 StringMap_t* additional_headers) { | |
500 URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface(); | |
501 VarInterface* var_interface = ppapi_->GetVarInterface(); | |
502 | |
503 PP_Resource request_info = interface->Create(ppapi_->GetInstance()); | |
504 if (!request_info) | |
505 return 0; | |
506 | |
507 interface->SetProperty( | |
508 request_info, PP_URLREQUESTPROPERTY_URL, | |
509 var_interface->VarFromUtf8(url.c_str(), url.length())); | |
510 interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_METHOD, | |
511 var_interface->VarFromUtf8(method, strlen(method))); | |
512 interface->SetProperty(request_info, | |
513 PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, | |
514 PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE)); | |
515 interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, | |
516 PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE)); | |
517 | |
518 // Merge the mount headers with the request headers. If the field is already | |
519 // set it |additional_headers|, don't use the one from headers_. | |
520 for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end(); | |
521 ++iter) { | |
522 const std::string& key = NormalizeHeaderKey(iter->first); | |
523 if (additional_headers->find(key) == additional_headers->end()) { | |
524 additional_headers->insert(std::make_pair(key, iter->second)); | |
525 } | |
526 } | |
527 | |
528 // Join the headers into one string. | |
529 std::string headers; | |
530 for (StringMap_t::iterator iter = additional_headers->begin(); | |
531 iter != additional_headers->end(); ++iter) { | |
532 headers += iter->first + ": " + iter->second + '\n'; | |
533 } | |
534 | |
535 interface->SetProperty( | |
536 request_info, PP_URLREQUESTPROPERTY_HEADERS, | |
537 var_interface->VarFromUtf8(headers.c_str(), headers.length())); | |
538 | |
539 return request_info; | |
540 } | |
541 | |
542 MountHttp::MountHttp() | |
543 : allow_cors_(false), | |
544 allow_credentials_(false), | |
545 allow_stat_cache_(true) { | |
546 } | |
547 | |
548 bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { | |
549 if (!Mount::Init(dev, args, ppapi)) | |
550 return false; | |
551 | |
552 // Parse mount args. | |
553 for (StringMap_t::iterator iter = args.begin(); iter != args.end(); ++iter) { | |
554 if (iter->first == "SOURCE") { | |
555 url_root_ = iter->second; | |
556 | |
557 // Make sure url_root_ ends with a slash. | |
558 if (!url_root_.empty() && url_root_[url_root_.length() - 1] != '/') { | |
559 url_root_ += '/'; | |
560 } | |
561 } else if (iter->first == "manifest") { | |
562 char *text = LoadManifest(iter->second); | |
563 if (text != NULL) { | |
564 ParseManifest(text); | |
565 delete[] text; | |
566 } | |
567 } else if (iter->first == "allow_cross_origin_requests") { | |
568 allow_cors_ = iter->second == "true"; | |
569 } else if (iter->first == "allow_credentials") { | |
570 allow_credentials_ = iter->second == "true"; | |
571 } else if (iter->first == "allow_stat_cache") { | |
572 allow_stat_cache_ = iter->second == "true"; | |
573 } else { | |
574 // Assume it is a header to pass to an HTTP request. | |
575 headers_[NormalizeHeaderKey(iter->first)] = iter->second; | |
576 } | |
577 } | |
578 | |
579 return true; | |
580 } | |
581 | |
582 void MountHttp::Destroy() { | |
583 } | |
584 | |
585 MountNodeDir* MountHttp::FindOrCreateDir(const Path& path) { | |
586 std::string strpath = path.Join(); | |
587 NodeMap_t::iterator iter = node_cache_.find(strpath); | |
588 if (iter != node_cache_.end()) { | |
589 return static_cast<MountNodeDir*>(iter->second); | |
590 } | |
591 | |
592 // If the node does not exist, create it, and add it to the node cache | |
593 MountNodeDir* node = new MountNodeDir(this); | |
594 node->Init(S_IREAD); | |
595 node_cache_[strpath] = node; | |
596 | |
597 // If not the root node, find the parent node and add it to the parent | |
598 if (!path.Top()) { | |
599 MountNodeDir* parent = FindOrCreateDir(path.Parent()); | |
600 parent->AddChild(path.Basename(), node); | |
601 } | |
602 | |
603 return node; | |
604 } | |
605 | |
606 bool MountHttp::ParseManifest(char *text) { | |
607 StringList_t lines; | |
608 SplitString(text, "\n", &lines); | |
609 | |
610 for (size_t i = 0; i < lines.size(); i++) { | |
611 StringList_t words; | |
612 SplitString(lines[i], " ", &words); | |
613 | |
614 if (words.size() == 3) { | |
615 char* modestr = words[0]; | |
616 char* lenstr = words[1]; | |
617 char* name = words[2]; | |
618 | |
619 assert(modestr && strlen(modestr) == 4); | |
620 assert(name && name[0] == '/'); | |
621 assert(lenstr); | |
622 | |
623 // Only support regular and streams for now | |
624 // Ignore EXEC bit | |
625 int mode = S_IFREG; | |
626 switch (modestr[0]) { | |
627 case '-': mode = S_IFREG; break; | |
628 case 'c': mode = S_IFCHR; break; | |
629 default: | |
630 fprintf(stderr, "Unable to parse type %s for %s.\n", modestr, name); | |
631 return false; | |
632 } | |
633 | |
634 switch (modestr[1]) { | |
635 case '-': break; | |
636 case 'r': mode |= S_IREAD; break; | |
637 default: | |
638 fprintf(stderr, "Unable to parse read %s for %s.\n", modestr, name); | |
639 return false; | |
640 } | |
641 | |
642 switch (modestr[2]) { | |
643 case '-': break; | |
644 case 'w': mode |= S_IWRITE; break; | |
645 default: | |
646 fprintf(stderr, "Unable to parse write %s for %s.\n", modestr, name); | |
647 return false; | |
648 } | |
649 | |
650 Path path(name); | |
651 std::string url = url_root_ + (path.IsAbsolute() ? | |
652 path.Range(1, path.Size()) : | |
653 path.Join()); | |
654 | |
655 MountNode* node = new MountNodeHttp(this, url); | |
656 node->Init(mode); | |
657 node->stat_.st_size = atoi(lenstr); | |
658 | |
659 MountNodeDir* dir_node = FindOrCreateDir(path.Parent()); | |
660 dir_node->AddChild(path.Basename(), node); | |
661 | |
662 std::string pname = path.Join(); | |
663 node_cache_[pname] = node; | |
664 } | |
665 } | |
666 | |
667 return true; | |
668 } | |
669 | |
670 char *MountHttp::LoadManifest(const std::string& manifestName) { | |
671 Path manifestPath(manifestName); | |
672 MountNode* manifiestNode = Open(manifestPath, O_RDONLY); | |
673 | |
674 if (manifiestNode) { | |
675 char *text = new char[manifiestNode->GetSize() + 1]; | |
676 off_t len = manifiestNode->Read(0, text, manifiestNode->GetSize()); | |
677 manifiestNode->Release(); | |
678 | |
679 text[len] = 0; | |
680 return text; | |
681 } | |
682 | |
683 fprintf(stderr, "Could not open manifest: %s\n", manifestName.c_str()); | |
684 return NULL; | |
685 } | |
OLD | NEW |