Commit eed34ebc authored by sbc's avatar sbc Committed by Commit bot

[NaCl SDK] nacl_io: Always create directory node in http filesystem root.

The root of all filesystems should be a directory node
except in the special case of blob URL where we maintain
the legacy behavior of having the mount point be a file
containing the blob content.

Also, fix the Access() call for http filesystem so that
it honors the access bits of the node itself.

This fixes an issue in naclports where a mount() of
an http filsystem followed by a chdir(http_root) would
always fail with ENOTDIR.

TEST=nacl_io_test

Review URL: https://codereview.chromium.org/547713002

Cr-Commit-Position: refs/heads/master@{#294000}
parent 3ba0480e
...@@ -42,11 +42,11 @@ std::string NormalizeHeaderKey(const std::string& s) { ...@@ -42,11 +42,11 @@ std::string NormalizeHeaderKey(const std::string& s) {
} }
Error HttpFs::Access(const Path& path, int a_mode) { Error HttpFs::Access(const Path& path, int a_mode) {
NodeMap_t::iterator iter = node_cache_.find(path.Join()); ScopedNode node = FindExistingNode(path);
if (iter == node_cache_.end()) { if (node.get() == NULL) {
// If we can't find the node in the cache, fetch it // If we can't find the node in the cache, fetch it
std::string url = MakeUrl(path); std::string url = MakeUrl(path);
ScopedNode node(new HttpFsNode(this, url, cache_content_)); node.reset(new HttpFsNode(this, url, cache_content_));
Error error = node->Init(0); Error error = node->Init(0);
if (error) if (error)
return error; return error;
...@@ -56,9 +56,12 @@ Error HttpFs::Access(const Path& path, int a_mode) { ...@@ -56,9 +56,12 @@ Error HttpFs::Access(const Path& path, int a_mode) {
return error; return error;
} }
// Don't allow write or execute access. int obj_mode = node->GetMode();
if (a_mode & (W_OK | X_OK)) if (((a_mode & R_OK) && !(obj_mode & S_IREAD)) ||
((a_mode & W_OK) && !(obj_mode & S_IWRITE)) ||
((a_mode & X_OK) && !(obj_mode & S_IEXEC))) {
return EACCES; return EACCES;
}
return 0; return 0;
} }
...@@ -66,15 +69,15 @@ Error HttpFs::Access(const Path& path, int a_mode) { ...@@ -66,15 +69,15 @@ Error HttpFs::Access(const Path& path, int a_mode) {
Error HttpFs::Open(const Path& path, int open_flags, ScopedNode* out_node) { Error HttpFs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
out_node->reset(NULL); out_node->reset(NULL);
NodeMap_t::iterator iter = node_cache_.find(path.Join()); ScopedNode node = FindExistingNode(path);
if (iter != node_cache_.end()) { if (node.get() != NULL) {
*out_node = iter->second; *out_node = node;
return 0; return 0;
} }
// If we can't find the node in the cache, create it // If we can't find the node in the cache, create it
std::string url = MakeUrl(path); std::string url = MakeUrl(path);
ScopedNode node(new HttpFsNode(this, url, cache_content_)); node.reset(new HttpFsNode(this, url, cache_content_));
Error error = node->Init(open_flags); Error error = node->Init(open_flags);
if (error) if (error)
return error; return error;
...@@ -97,48 +100,54 @@ Error HttpFs::Open(const Path& path, int open_flags, ScopedNode* out_node) { ...@@ -97,48 +100,54 @@ Error HttpFs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
return 0; return 0;
} }
Error HttpFs::Unlink(const Path& path) { ScopedNode HttpFs::FindExistingNode(const Path& path) {
NodeMap_t::iterator iter = node_cache_.find(path.Join()); NodeMap_t::iterator iter = node_cache_.find(path.Join());
if (iter == node_cache_.end()) if (iter == node_cache_.end())
return ScopedNode();
return iter->second;
}
Error HttpFs::Unlink(const Path& path) {
ScopedNode node = FindExistingNode(path);
if (node.get() == NULL)
return ENOENT; return ENOENT;
if (iter->second->IsaDir()) if (node->IsaDir())
return EISDIR; return EISDIR;
return EACCES; return EACCES;
} }
Error HttpFs::Mkdir(const Path& path, int permissions) { Error HttpFs::Mkdir(const Path& path, int permissions) {
NodeMap_t::iterator iter = node_cache_.find(path.Join()); ScopedNode node = FindExistingNode(path);
if (iter != node_cache_.end()) { if (node.get() != NULL && node->IsaDir())
if (iter->second->IsaDir()) return EEXIST;
return EEXIST;
}
return EACCES; return EACCES;
} }
Error HttpFs::Rmdir(const Path& path) { Error HttpFs::Rmdir(const Path& path) {
NodeMap_t::iterator iter = node_cache_.find(path.Join()); ScopedNode node = FindExistingNode(path);
if (iter == node_cache_.end()) if (node.get() == NULL)
return ENOENT; return ENOENT;
if (!iter->second->IsaDir()) if (!node->IsaDir())
return ENOTDIR; return ENOTDIR;
return EACCES; return EACCES;
} }
Error HttpFs::Remove(const Path& path) { Error HttpFs::Remove(const Path& path) {
NodeMap_t::iterator iter = node_cache_.find(path.Join()); ScopedNode node = FindExistingNode(path);
if (iter == node_cache_.end()) if (node.get() == NULL)
return ENOENT; return ENOENT;
return EACCES; return EACCES;
} }
Error HttpFs::Rename(const Path& path, const Path& newpath) { Error HttpFs::Rename(const Path& path, const Path& newpath) {
NodeMap_t::iterator iter = node_cache_.find(path.Join()); ScopedNode node = FindExistingNode(path);
if (iter == node_cache_.end()) if (node.get() == NULL)
return ENOENT; return ENOENT;
return EACCES; return EACCES;
...@@ -197,7 +206,8 @@ HttpFs::HttpFs() ...@@ -197,7 +206,8 @@ HttpFs::HttpFs()
: allow_cors_(false), : allow_cors_(false),
allow_credentials_(false), allow_credentials_(false),
cache_stat_(true), cache_stat_(true),
cache_content_(true) { cache_content_(true),
is_blob_url_(false) {
} }
Error HttpFs::Init(const FsInitArgs& args) { Error HttpFs::Init(const FsInitArgs& args) {
...@@ -213,11 +223,6 @@ Error HttpFs::Init(const FsInitArgs& args) { ...@@ -213,11 +223,6 @@ Error HttpFs::Init(const FsInitArgs& args) {
url_root_ = iter->second; url_root_ = iter->second;
is_blob_url_ = strncmp(url_root_.c_str(), "blob:", 5) == 0; is_blob_url_ = strncmp(url_root_.c_str(), "blob:", 5) == 0;
// Make sure url_root_ ends with a slash, except for blob URLs.
if (!is_blob_url_ && !url_root_.empty() &&
url_root_[url_root_.length() - 1] != '/') {
url_root_ += '/';
}
} else if (iter->first == "manifest") { } else if (iter->first == "manifest") {
char* text; char* text;
error = LoadManifest(iter->second, &text); error = LoadManifest(iter->second, &text);
...@@ -245,6 +250,18 @@ Error HttpFs::Init(const FsInitArgs& args) { ...@@ -245,6 +250,18 @@ Error HttpFs::Init(const FsInitArgs& args) {
} }
} }
if (!is_blob_url_) {
if (!url_root_.empty() && url_root_[url_root_.length() - 1] != '/') {
// Make sure url_root_ ends with a slash, except for blob URLs.
url_root_ += '/';
}
ScopedNode root;
error = FindOrCreateDir(Path("/"), &root);
if (error)
return error;
}
return 0; return 0;
} }
...@@ -253,18 +270,20 @@ void HttpFs::Destroy() { ...@@ -253,18 +270,20 @@ void HttpFs::Destroy() {
Error HttpFs::FindOrCreateDir(const Path& path, ScopedNode* out_node) { Error HttpFs::FindOrCreateDir(const Path& path, ScopedNode* out_node) {
out_node->reset(NULL); out_node->reset(NULL);
std::string strpath = path.Join();
NodeMap_t::iterator iter = node_cache_.find(strpath); ScopedNode node = FindExistingNode(path);
if (iter != node_cache_.end()) { if (node.get() != NULL) {
*out_node = iter->second; *out_node = node;
return 0; return 0;
} }
// If the node does not exist, create it. // If the node does not exist, create it.
ScopedNode node(new DirNode(this)); node.reset(new DirNode(this));
Error error = node->Init(0); Error error = node->Init(0);
if (error) if (error)
return error; return error;
// Directorys in http filesystems are never writable.
node->SetMode(node->GetMode() & ~S_IWALL);
// If not the root node, find the parent node and add it to the parent // If not the root node, find the parent node and add it to the parent
if (!path.IsRoot()) { if (!path.IsRoot()) {
...@@ -279,7 +298,7 @@ Error HttpFs::FindOrCreateDir(const Path& path, ScopedNode* out_node) { ...@@ -279,7 +298,7 @@ Error HttpFs::FindOrCreateDir(const Path& path, ScopedNode* out_node) {
} }
// Add it to the node cache. // Add it to the node cache.
node_cache_[strpath] = node; node_cache_[path.Join()] = node;
*out_node = node; *out_node = node;
return 0; return 0;
} }
...@@ -311,13 +330,13 @@ Error HttpFs::ParseManifest(const char* text) { ...@@ -311,13 +330,13 @@ Error HttpFs::ParseManifest(const char* text) {
// Only support regular and streams for now // Only support regular and streams for now
// Ignore EXEC bit // Ignore EXEC bit
int mode = S_IFREG; int type = 0;
switch (modestr[0]) { switch (modestr[0]) {
case '-': case '-':
mode = S_IFREG; type = S_IFREG;
break; break;
case 'c': case 'c':
mode = S_IFCHR; type = S_IFCHR;
break; break;
default: default:
LOG_ERROR("Unable to parse type %s for %s.", LOG_ERROR("Unable to parse type %s for %s.",
...@@ -326,6 +345,7 @@ Error HttpFs::ParseManifest(const char* text) { ...@@ -326,6 +345,7 @@ Error HttpFs::ParseManifest(const char* text) {
return EINVAL; return EINVAL;
} }
int mode = 0;
switch (modestr[1]) { switch (modestr[1]) {
case '-': case '-':
break; break;
...@@ -356,8 +376,9 @@ Error HttpFs::ParseManifest(const char* text) { ...@@ -356,8 +376,9 @@ Error HttpFs::ParseManifest(const char* text) {
std::string url = MakeUrl(path); std::string url = MakeUrl(path);
HttpFsNode* http_node = new HttpFsNode(this, url, cache_content_); HttpFsNode* http_node = new HttpFsNode(this, url, cache_content_);
http_node->SetMode(mode);
ScopedNode node(http_node); ScopedNode node(http_node);
node->SetMode(mode);
node->SetType(type);
Error error = node->Init(0); Error error = node->Init(0);
if (error) if (error)
...@@ -373,8 +394,7 @@ Error HttpFs::ParseManifest(const char* text) { ...@@ -373,8 +394,7 @@ Error HttpFs::ParseManifest(const char* text) {
if (error) if (error)
return error; return error;
std::string pname = path.Join(); node_cache_[path.Join()] = node;
node_cache_[pname] = node;
} }
} }
......
...@@ -35,6 +35,7 @@ class HttpFs : public Filesystem { ...@@ -35,6 +35,7 @@ class HttpFs : public Filesystem {
virtual Error Init(const FsInitArgs& args); virtual Error Init(const FsInitArgs& args);
virtual void Destroy(); virtual void Destroy();
ScopedNode FindExistingNode(const Path& path);
Error FindOrCreateDir(const Path& path, ScopedNode* out_node); Error FindOrCreateDir(const Path& path, ScopedNode* out_node);
Error LoadManifest(const std::string& path, char** out_manifest); Error LoadManifest(const std::string& path, char** out_manifest);
Error ParseManifest(const char* text); Error ParseManifest(const char* text);
......
...@@ -232,10 +232,8 @@ HttpFsNode::HttpFsNode(Filesystem* filesystem, ...@@ -232,10 +232,8 @@ HttpFsNode::HttpFsNode(Filesystem* filesystem,
buffer_len_(0), buffer_len_(0),
cache_content_(cache_content), cache_content_(cache_content),
has_cached_size_(false) { has_cached_size_(false) {
} // http nodes are read-only by default
SetMode(S_IRALL);
void HttpFsNode::SetMode(int mode) {
stat_.st_mode = mode;
} }
Error HttpFsNode::GetStat_Locked(struct stat* stat) { Error HttpFsNode::GetStat_Locked(struct stat* stat) {
...@@ -297,7 +295,7 @@ Error HttpFsNode::GetStat_Locked(struct stat* stat) { ...@@ -297,7 +295,7 @@ Error HttpFsNode::GetStat_Locked(struct stat* stat) {
stat_.st_mtime = 0; stat_.st_mtime = 0;
stat_.st_ctime = 0; stat_.st_ctime = 0;
stat_.st_mode |= S_IFREG; SetType(S_IFREG);
} }
// Fill the stat structure if provided // Fill the stat structure if provided
......
...@@ -37,7 +37,6 @@ class HttpFsNode : public Node { ...@@ -37,7 +37,6 @@ class HttpFsNode : public Node {
virtual Error GetSize(off_t* out_size); virtual Error GetSize(off_t* out_size);
void SetCachedSize(off_t size); void SetCachedSize(off_t size);
void SetMode(int mode);
protected: protected:
HttpFsNode(Filesystem* filesystem, HttpFsNode(Filesystem* filesystem,
......
...@@ -199,6 +199,12 @@ void Node::SetType(int type) { ...@@ -199,6 +199,12 @@ void Node::SetType(int type) {
stat_.st_mode |= type; stat_.st_mode |= type;
} }
void Node::SetMode(int mode) {
assert((mode & S_IFMT) == 0);
stat_.st_mode &= S_IFMT;
stat_.st_mode |= mode;
}
bool Node::IsaDir() { bool Node::IsaDir() {
return GetType() == S_IFDIR; return GetType() == S_IFDIR;
} }
......
...@@ -92,6 +92,7 @@ class Node : public sdk_util::RefObject { ...@@ -92,6 +92,7 @@ class Node : public sdk_util::RefObject {
virtual int GetLinks(); virtual int GetLinks();
virtual int GetMode(); virtual int GetMode();
virtual void SetMode(int mode);
virtual int GetType(); virtual int GetType();
virtual void SetType(int type); virtual void SetType(int type);
// Assume that |out_size| is non-NULL. // Assume that |out_size| is non-NULL.
......
...@@ -178,8 +178,7 @@ TEST_P(HttpFsTest, GetStat) { ...@@ -178,8 +178,7 @@ TEST_P(HttpFsTest, GetStat) {
struct stat statbuf; struct stat statbuf;
EXPECT_EQ(0, node->GetStat(&statbuf)); EXPECT_EQ(0, node->GetStat(&statbuf));
EXPECT_EQ(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, EXPECT_EQ(S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, statbuf.st_mode);
statbuf.st_mode);
EXPECT_EQ(strlen(contents), statbuf.st_size); EXPECT_EQ(strlen(contents), statbuf.st_size);
// These are not currently set. // These are not currently set.
EXPECT_EQ(0, statbuf.st_atime); EXPECT_EQ(0, statbuf.st_atime);
...@@ -253,8 +252,7 @@ TEST_P(HttpFsLargeFileTest, GetStat) { ...@@ -253,8 +252,7 @@ TEST_P(HttpFsLargeFileTest, GetStat) {
struct stat statbuf; struct stat statbuf;
EXPECT_EQ(0, node->GetStat(&statbuf)); EXPECT_EQ(0, node->GetStat(&statbuf));
EXPECT_EQ(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, EXPECT_EQ(S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, statbuf.st_mode);
statbuf.st_mode);
EXPECT_EQ(size, statbuf.st_size); EXPECT_EQ(size, statbuf.st_size);
// These are not currently set. // These are not currently set.
EXPECT_EQ(0, statbuf.st_atime); EXPECT_EQ(0, statbuf.st_atime);
...@@ -270,6 +268,21 @@ INSTANTIATE_TEST_CASE_P(Default, ...@@ -270,6 +268,21 @@ INSTANTIATE_TEST_CASE_P(Default,
::testing::Values((uint32_t)kStringMapParamCacheNone, ::testing::Values((uint32_t)kStringMapParamCacheNone,
(uint32_t)kStringMapParamCacheStat)); (uint32_t)kStringMapParamCacheStat));
TEST(HttpFsDirTest, Root) {
StringMap_t args;
HttpFsForTesting fs(args, NULL);
// Check root node is directory
ScopedNode node;
ASSERT_EQ(0, fs.Open(Path("/"), O_RDONLY, &node));
ASSERT_TRUE(node->IsaDir());
// We have to r+w access to the root node
ASSERT_EQ(0, fs.Access(Path("/"), R_OK));
ASSERT_EQ(0, fs.Access(Path("/"), X_OK));
ASSERT_EQ(EACCES, fs.Access(Path("/"), W_OK));
}
TEST(HttpFsDirTest, Mkdir) { TEST(HttpFsDirTest, Mkdir) {
StringMap_t args; StringMap_t args;
HttpFsForTesting fs(args, NULL); HttpFsForTesting fs(args, NULL);
...@@ -369,24 +382,30 @@ TEST(HttpFsDirTest, ParseManifest) { ...@@ -369,24 +382,30 @@ TEST(HttpFsDirTest, ParseManifest) {
} }
TEST(HttpFsBlobUrlTest, Basic) { TEST(HttpFsBlobUrlTest, Basic) {
const char* kUrl = const char* kUrl = "blob:http%3A//example.com/6b87a5a6-713e";
"blob:http%3A//example.com/6b87a5a6-713e-46a4-9f0c-78066406455d"; const char* kContent = "hello";
FakePepperInterfaceURLLoader ppapi; FakePepperInterfaceURLLoader ppapi;
ASSERT_TRUE(ppapi.server_template()->SetBlobEntity(kUrl, "", NULL)); ASSERT_TRUE(ppapi.server_template()->SetBlobEntity(kUrl, kContent, NULL));
StringMap_t args; StringMap_t args;
args["SOURCE"] = kUrl; args["SOURCE"] = kUrl;
HttpFsForTesting fs(args, &ppapi); HttpFsForTesting fs(args, &ppapi);
// We have to read from the mount root to read a Blob URL. // Check access to root folder
ASSERT_EQ(0, fs.Access(Path("/"), R_OK)); ASSERT_EQ(0, fs.Access(Path("/"), R_OK));
ASSERT_EQ(EACCES, fs.Access(Path("/"), W_OK)); ASSERT_EQ(EACCES, fs.Access(Path("/"), W_OK));
ASSERT_EQ(EACCES, fs.Access(Path("/"), X_OK)); ASSERT_EQ(EACCES, fs.Access(Path("/"), X_OK));
// Any other path will fail. // Any other path will fail.
ScopedNode foo; ScopedNode foo;
ASSERT_EQ(ENOENT, fs.Access(Path(""), R_OK)); ASSERT_EQ(ENOENT, fs.Access(Path("/blah"), R_OK));
ASSERT_EQ(ENOENT, fs.Access(Path("."), R_OK));
ASSERT_EQ(ENOENT, fs.Access(Path("blah"), R_OK)); // Verify file size
ScopedNode node;
struct stat statbuf;
ASSERT_EQ(0, fs.Open(Path("/"), O_RDONLY, &node));
ASSERT_EQ(0, node->GetStat(&statbuf));
ASSERT_EQ(0, node->GetStat(&statbuf));
ASSERT_EQ(strlen(kContent), statbuf.st_size);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment