diff options
author | sbc@chromium.org <sbc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-22 00:03:53 +0000 |
---|---|---|
committer | sbc@chromium.org <sbc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-22 00:03:53 +0000 |
commit | 32a3e53d38529dd409a090ddb21eec37b5864371 (patch) | |
tree | 72c357bf27a9c45381d10000f68c2da67a7b2ce2 /native_client_sdk/src | |
parent | 52c39f44ccf1ded7c55588740fd6482b3306ff2e (diff) | |
download | chromium_src-32a3e53d38529dd409a090ddb21eec37b5864371.zip chromium_src-32a3e53d38529dd409a090ddb21eec37b5864371.tar.gz chromium_src-32a3e53d38529dd409a090ddb21eec37b5864371.tar.bz2 |
[NaCl SDK] nacl_io: Make files world writable by default.
All files by default now have 'group' and 'other'
permission to match the user permission.
Also, for most cases (all except for html5fs) don't
derive the stat mode from the initial call to open().
Improve/correct handling of access mode such that O_RDONLY
and O_WRONLY are no honored in the kernel handle.
This change was motivated by thttpd in naclports
which will only serve files that are world readable.
R=binji@chromium.org
Review URL: https://codereview.chromium.org/30153002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@229962 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk/src')
25 files changed, 215 insertions, 151 deletions
diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc index eab3ce8..c3b6bc1 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc @@ -39,6 +39,10 @@ MountNodeSocket* KernelHandle::socket_node() { Error KernelHandle::Init(int open_flags) { handle_attr_.flags = open_flags; + if (!node_->CanOpen(open_flags)) { + return EACCES; + } + if (open_flags & O_APPEND) { Error error = node_->GetSize(&handle_attr_.offs); if (error) @@ -92,6 +96,8 @@ Error KernelHandle::Seek(off_t offset, int whence, off_t* out_offset) { Error KernelHandle::Read(void* buf, size_t nbytes, int* cnt) { AUTO_LOCK(handle_lock_); + if (OpenMode() == O_WRONLY) + return EACCES; Error error = node_->Read(handle_attr_, buf, nbytes, cnt); if (0 == error) handle_attr_.offs += *cnt; @@ -100,6 +106,8 @@ Error KernelHandle::Read(void* buf, size_t nbytes, int* cnt) { Error KernelHandle::Write(const void* buf, size_t nbytes, int* cnt) { AUTO_LOCK(handle_lock_); + if (OpenMode() == O_RDONLY) + return EACCES; Error error = node_->Write(handle_attr_, buf, nbytes, cnt); if (0 == error) handle_attr_.offs += *cnt; diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h index 188ae2c..d445f40 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h @@ -76,6 +76,8 @@ class KernelHandle : public sdk_util::RefObject { const HandleAttr& Attr() { return handle_attr_; } + int OpenMode() { return handle_attr_.flags & 3; } + private: // Returns the MountNodeSocket* if this node is a socket otherwise returns // NULL. diff --git a/native_client_sdk/src/libraries/nacl_io/mount.cc b/native_client_sdk/src/libraries/nacl_io/mount.cc index 92f977a..4892662 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount.cc @@ -39,19 +39,6 @@ Error Mount::OpenResource(const Path& path, ScopedMountNode* out_node) { return EINVAL; } -int Mount::OpenFlagsToPermission(int open_flags) { - int out; - switch (open_flags & 3) { - case O_RDONLY: - out = S_IREAD; - case O_WRONLY: - out = S_IWRITE; - case O_RDWR: - out = S_IREAD | S_IWRITE; - } - return out; -} - void Mount::OnNodeCreated(MountNode* node) { node->stat_.st_ino = inode_pool_.Acquire(); node->stat_.st_dev = dev_; diff --git a/native_client_sdk/src/libraries/nacl_io/mount.h b/native_client_sdk/src/libraries/nacl_io/mount.h index e480d89..65a6e7a 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.h +++ b/native_client_sdk/src/libraries/nacl_io/mount.h @@ -69,9 +69,6 @@ class Mount : public sdk_util::RefObject { virtual Error Rmdir(const Path& path) = 0; virtual Error Remove(const Path& path) = 0; - // Convert from R,W,R/W open flags to STAT permissions. - static int OpenFlagsToPermission(int open_flags); - // Assumes that |node| is non-NULL. void OnNodeCreated(MountNode* node); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc index b0c2d5b..fdc6379 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc @@ -110,7 +110,7 @@ class UrandomNode : public MountNode { }; RealNode::RealNode(Mount* mount, int fd) : MountNode(mount), fd_(fd) { - stat_.st_mode = S_IFCHR; + SetType(S_IFCHR); } Error RealNode::Read(const HandleAttr& attr, @@ -186,7 +186,7 @@ Error ConsoleNode::Write(const HandleAttr& attr, return 0; } -ZeroNode::ZeroNode(Mount* mount) : MountNode(mount) { stat_.st_mode = S_IFCHR; } +ZeroNode::ZeroNode(Mount* mount) : MountNode(mount) { SetType(S_IFCHR); } Error ZeroNode::Read(const HandleAttr& attr, void* buf, @@ -206,7 +206,7 @@ Error ZeroNode::Write(const HandleAttr& attr, } UrandomNode::UrandomNode(Mount* mount) : MountNode(mount) { - stat_.st_mode = S_IFCHR; + SetType(S_IFCHR); #if defined(__native_client__) size_t result = nacl_interface_query( NACL_IRT_RANDOM_v0_1, &random_interface_, sizeof(random_interface_)); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc index f4d9a6dd..da8b4bb 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc @@ -157,11 +157,11 @@ Error MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { filesystem_open_error_ = PPErrorToErrno(result); return filesystem_open_error_; - } else { - // We have to assume the call to Open will succeed; there is no better - // result to return here. - return 0; } + + // We have to assume the call to Open will succeed; there is no better + // result to return here. + return 0; } void MountHtml5Fs::Destroy() { diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_http.cc index 6ced25d..2d0eaa0 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.cc @@ -47,7 +47,7 @@ Error MountHttp::Access(const Path& path, int a_mode) { // If we can't find the node in the cache, fetch it std::string url = MakeUrl(path); ScopedMountNode node(new MountNodeHttp(this, url, cache_content_)); - Error error = node->Init(O_RDONLY); + Error error = node->Init(0); if (error) return error; @@ -63,7 +63,8 @@ Error MountHttp::Access(const Path& path, int a_mode) { return 0; } -Error MountHttp::Open(const Path& path, int flags, ScopedMountNode* out_node) { +Error MountHttp::Open(const Path& path, int open_flags, + ScopedMountNode* out_node) { out_node->reset(NULL); assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); @@ -76,7 +77,7 @@ Error MountHttp::Open(const Path& path, int flags, ScopedMountNode* out_node) { // If we can't find the node in the cache, create it std::string url = MakeUrl(path); ScopedMountNode node(new MountNodeHttp(this, url, cache_content_)); - Error error = node->Init(OpenFlagsToPermission(flags)); + Error error = node->Init(open_flags); if (error) return error; @@ -250,7 +251,7 @@ Error MountHttp::FindOrCreateDir(const Path& path, // If the node does not exist, create it. ScopedMountNode node(new MountNodeDir(this)); - Error error = node->Init(S_IREAD); + Error error = node->Init(0); if (error) return error; @@ -316,7 +317,7 @@ Error MountHttp::ParseManifest(const char* text) { case '-': break; case 'r': - mode |= S_IREAD; + mode |= S_IRUSR | S_IRGRP | S_IROTH; break; default: fprintf(stderr, "Unable to parse read %s for %s.\n", modestr.c_str(), @@ -328,7 +329,7 @@ Error MountHttp::ParseManifest(const char* text) { case '-': break; case 'w': - mode |= S_IWRITE; + mode |= S_IWUSR | S_IWGRP | S_IWOTH; break; default: fprintf(stderr, "Unable to parse write %s for %s.\n", modestr.c_str(), @@ -340,9 +341,10 @@ Error MountHttp::ParseManifest(const char* text) { std::string url = MakeUrl(path); MountNodeHttp* http_node = new MountNodeHttp(this, url, cache_content_); + http_node->SetMode(mode); ScopedMountNode node(http_node); - Error error = node->Init(mode); + Error error = node->Init(0); if (error) return error; http_node->SetCachedSize(atoi(lenstr.c_str())); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc index 2959599..9b32cc1 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc @@ -29,7 +29,7 @@ Error MountMem::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { return error; root_.reset(new MountNodeDir(this)); - error = root_->Init(S_IREAD | S_IWRITE | S_IEXEC); + error = root_->Init(0); if (error) { root_.reset(NULL); return error; @@ -93,14 +93,15 @@ Error MountMem::Access(const Path& path, int a_mode) { return 0; } -Error MountMem::Open(const Path& path, int flags, ScopedMountNode* out_node) { +Error MountMem::Open(const Path& path, int open_flags, + ScopedMountNode* out_node) { out_node->reset(NULL); ScopedMountNode node; Error error = FindNode(path, 0, &node); if (error) { // If the node does not exist and we can't create it, fail - if ((flags & O_CREAT) == 0) + if ((open_flags & O_CREAT) == 0) return ENOENT; // Now first find the parent directory to see if we can add it @@ -110,7 +111,7 @@ Error MountMem::Open(const Path& path, int flags, ScopedMountNode* out_node) { return error; node.reset(new MountNodeMem(this)); - error = node->Init(OpenFlagsToPermission(flags)); + error = node->Init(open_flags); if (error) return error; @@ -123,19 +124,13 @@ Error MountMem::Open(const Path& path, int flags, ScopedMountNode* out_node) { } // Directories can only be opened read-only. - if (node->IsaDir() && (flags & 3) != O_RDONLY) + if (node->IsaDir() && (open_flags & 3) != O_RDONLY) return EISDIR; // If we were expected to create it exclusively, fail - if (flags & O_EXCL) + if (open_flags & O_EXCL) return EEXIST; - // Verify we got the requested permissions. - int req_mode = OpenFlagsToPermission(flags); - int obj_mode = node->GetMode() & (S_IREAD | S_IWRITE); - if ((obj_mode & req_mode) != req_mode) - return EACCES; - *out_node = node; return 0; } @@ -166,7 +161,7 @@ Error MountMem::Mkdir(const Path& path, int mode) { // it will get ref counted again. In either case, release the // recount we have on exit. node.reset(new MountNodeDir(this)); - error = node->Init(S_IREAD | S_IWRITE | S_IEXEC); + error = node->Init(0); if (error) return error; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.cc b/native_client_sdk/src/libraries/nacl_io/mount_node.cc index 787db32..f69225d 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.cc @@ -4,6 +4,7 @@ #include "nacl_io/mount_node.h" +#include <assert.h> #include <errno.h> #include <fcntl.h> #include <poll.h> @@ -28,6 +29,7 @@ MountNode::MountNode(Mount* mount) : mount_(mount) { memset(&stat_, 0, sizeof(stat_)); stat_.st_gid = GRP_ID; stat_.st_uid = USR_ID; + stat_.st_mode = S_IRALL | S_IWALL; // Mount should normally never be NULL, but may be null in tests. // If NULL, at least set the inode to a valid (nonzero) value. @@ -39,8 +41,7 @@ MountNode::MountNode(Mount* mount) : mount_(mount) { MountNode::~MountNode() {} -Error MountNode::Init(int mode) { - stat_.st_mode |= mode; +Error MountNode::Init(int open_flags) { return 0; } @@ -59,6 +60,19 @@ uint32_t MountNode::GetEventStatus() { return POLLIN | POLLOUT; } +bool MountNode::CanOpen(int open_flags) { + switch (open_flags & 3) { + case O_RDONLY: + return (stat_.st_mode & S_IRALL) != 0; + case O_WRONLY: + return (stat_.st_mode & S_IWALL) != 0; + case O_RDWR: + return (stat_.st_mode & S_IRALL) != 0 && (stat_.st_mode & S_IWALL) != 0; + } + + return false; +} + Error MountNode::FSync() { return 0; } Error MountNode::FTruncate(off_t length) { return EINVAL; } @@ -165,6 +179,12 @@ Error MountNode::GetSize(size_t* out_size) { int MountNode::GetType() { return stat_.st_mode & S_IFMT; } +void MountNode::SetType(int type) { + assert((type & ~S_IFMT) == 0); + stat_.st_mode &= ~S_IFMT; + stat_.st_mode |= type; +} + bool MountNode::IsaDir() { return (stat_.st_mode & S_IFDIR) != 0; } bool MountNode::IsaFile() { return (stat_.st_mode & S_IFREG) != 0; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.h b/native_client_sdk/src/libraries/nacl_io/mount_node.h index e998f2b..fe5493c 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.h @@ -18,6 +18,10 @@ #include "sdk_util/scoped_ref.h" #include "sdk_util/simple_lock.h" +#define S_IRALL (S_IRUSR | S_IRGRP | S_IROTH) +#define S_IWALL (S_IWUSR | S_IWGRP | S_IWOTH) +#define S_IXALL (S_IXUSR | S_IXGRP | S_IXOTH) + namespace nacl_io { class Mount; @@ -34,11 +38,13 @@ class MountNode : public sdk_util::RefObject { virtual ~MountNode(); protected: - // Initialize with stat mode flags - virtual Error Init(int mode); + virtual Error Init(int open_flags); virtual void Destroy(); public: + // Return true if the node permissions match the given open mode. + virtual bool CanOpen(int open_flags); + // Returns the emitter for this Node if it has one, if not, assume this // object can not block. virtual EventEmitter* GetEventEmitter(); @@ -86,6 +92,7 @@ class MountNode : public sdk_util::RefObject { virtual int GetLinks(); virtual int GetMode(); virtual int GetType(); + virtual void SetType(int type); // Assume that |out_size| is non-NULL. virtual Error GetSize(size_t* out_size); virtual bool IsaDir(); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_char.h b/native_client_sdk/src/libraries/nacl_io/mount_node_char.h index 48ad86f..da34483 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_char.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_char.h @@ -12,7 +12,7 @@ namespace nacl_io { class MountNodeCharDevice : public MountNode { public: explicit MountNodeCharDevice(Mount* mount) : MountNode(mount) { - stat_.st_mode = S_IFCHR; + SetType(S_IFCHR); } }; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc index 4ce1c72..5a71de8 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc @@ -25,7 +25,9 @@ MountNodeDir::MountNodeDir(Mount* mount) : MountNode(mount), cache_(stat_.st_ino, kParentDirIno), cache_built_(false) { - stat_.st_mode |= (S_IFDIR | S_IREAD | S_IWRITE | S_IEXEC); + SetType(S_IFDIR); + // Directories are raadable, writable and executable by default. + stat_.st_mode |= S_IRALL | S_IWALL | S_IXALL; } MountNodeDir::~MountNodeDir() { diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc index 19ebeaa..526f94f 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc @@ -262,7 +262,7 @@ MountNodeHtml5Fs::MountNodeHtml5Fs(Mount* mount, PP_Resource fileref_resource) fileio_resource_(0) {} Error MountNodeHtml5Fs::Init(int open_flags) { - Error error = MountNode::Init(Mount::OpenFlagsToPermission(open_flags)); + Error error = MountNode::Init(open_flags); if (error) return error; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc index a9ff7ca..d6355ad 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc @@ -275,6 +275,10 @@ MountNodeHttp::MountNodeHttp(Mount* mount, cache_content_(cache_content), has_cached_size_(false) {} +void MountNodeHttp::SetMode(int mode) { + stat_.st_mode = mode; +} + Error MountNodeHttp::OpenUrl(const char* method, StringMap_t* request_headers, PP_Resource* out_loader, diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.h b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h index f920907..0e083b4 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_http.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h @@ -37,6 +37,7 @@ class MountNodeHttp : public MountNode { virtual Error GetSize(size_t* out_size); void SetCachedSize(off_t size); + void SetMode(int mode); protected: MountNodeHttp(Mount* mount, const std::string& url, bool cache_content); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc index bd983be..5ae7095 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc @@ -25,7 +25,7 @@ const size_t kMaxResizeIncrement = 16 * 1024 * 1024; MountNodeMem::MountNodeMem(Mount* mount) : MountNode(mount) { - stat_.st_mode |= S_IFREG; + SetType(S_IFREG); } MountNodeMem::~MountNodeMem() {} diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_socket.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_socket.cc index 0571659..12c5b9d 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_socket.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_socket.cc @@ -25,7 +25,7 @@ MountNodeSocket::MountNodeSocket(Mount* mount) remote_addr_(0), socket_flags_(0), last_errno_(0) { - stat_.st_mode |= S_IFSOCK; + SetType(S_IFSOCK); } MountNodeSocket::MountNodeSocket(Mount* mount, PP_Resource socket) @@ -35,7 +35,7 @@ MountNodeSocket::MountNodeSocket(Mount* mount, PP_Resource socket) remote_addr_(0), socket_flags_(0), last_errno_(0) { - stat_.st_mode |= S_IFSOCK; + SetType(S_IFSOCK); mount_->ppapi()->AddRefResource(socket_resource_); } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_stream.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_stream.cc index 0623976..d38a69e 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_stream.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_stream.cc @@ -24,7 +24,7 @@ MountNodeStream::MountNodeStream(Mount* mnt) } Error MountNodeStream::Init(int open_flags) { - MountNode::Init(Mount::OpenFlagsToPermission(open_flags)); + MountNode::Init(open_flags); if (open_flags & O_NONBLOCK) SetStreamFlags(SSF_NON_BLOCK); diff --git a/native_client_sdk/src/tests/nacl_io_test/event_test.cc b/native_client_sdk/src/tests/nacl_io_test/event_test.cc index f04ec08..92d9e74 100644 --- a/native_client_sdk/src/tests/nacl_io_test/event_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/event_test.cc @@ -271,23 +271,25 @@ TEST_F(SelectPollTest, PollMemPipe) { // Both FDs for regular files should be read/write but not exception. fds[0] = kp->open("/test.txt", O_CREAT | O_WRONLY); fds[1] = kp->open("/test.txt", O_RDONLY); + ASSERT_GT(fds[0], -1); + ASSERT_GT(fds[1], -1); SetFDs(fds, 2); - EXPECT_EQ(2, kp->poll(pollfds, 2, 0)); - EXPECT_EQ(POLLIN | POLLOUT, pollfds[0].revents); - EXPECT_EQ(POLLIN | POLLOUT, pollfds[1].revents); + ASSERT_EQ(2, kp->poll(pollfds, 2, 0)); + ASSERT_EQ(POLLIN | POLLOUT, pollfds[0].revents); + ASSERT_EQ(POLLIN | POLLOUT, pollfds[1].revents); CloseFDs(fds, 2); // The write FD should select for write-only, read FD should not select - EXPECT_EQ(0, kp->pipe(fds)); + ASSERT_EQ(0, kp->pipe(fds)); SetFDs(fds, 2); - EXPECT_EQ(2, kp->poll(pollfds, 2, 0)); + ASSERT_EQ(2, kp->poll(pollfds, 2, 0)); // TODO(noelallen) fix poll based on open mode // EXPECT_EQ(0, pollfds[0].revents); // Bug 291018 - EXPECT_EQ(POLLOUT, pollfds[1].revents); + ASSERT_EQ(POLLOUT, pollfds[1].revents); CloseFDs(fds, 2); } @@ -298,9 +300,11 @@ TEST_F(SelectPollTest, SelectMemPipe) { // Both FDs for regular files should be read/write but not exception. fds[0] = kp->open("/test.txt", O_CREAT | O_WRONLY); fds[1] = kp->open("/test.txt", O_RDONLY); + ASSERT_GT(fds[0], -1); + ASSERT_GT(fds[1], -1); SetFDs(fds, 2); - EXPECT_EQ(4, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); + ASSERT_EQ(4, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); EXPECT_NE(0, FD_ISSET(fds[0], &rd_set)); EXPECT_NE(0, FD_ISSET(fds[1], &rd_set)); EXPECT_NE(0, FD_ISSET(fds[0], &wr_set)); @@ -311,10 +315,10 @@ TEST_F(SelectPollTest, SelectMemPipe) { CloseFDs(fds, 2); // The write FD should select for write-only, read FD should not select - EXPECT_EQ(0, kp->pipe(fds)); + ASSERT_EQ(0, kp->pipe(fds)); SetFDs(fds, 2); - EXPECT_EQ(2, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); + ASSERT_EQ(2, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); EXPECT_EQ(0, FD_ISSET(fds[0], &rd_set)); EXPECT_EQ(0, FD_ISSET(fds[1], &rd_set)); // TODO(noelallen) fix poll based on open mode diff --git a/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc index 4ec3136..d86bd93 100644 --- a/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc @@ -184,4 +184,3 @@ TEST_F(KernelObjectTest, FreeAndReassignFD) { EXPECT_EQ(3, raw_handle->RefCount()); EXPECT_EQ(raw_handle, handle.get()); } - diff --git a/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc index e2e416d..a7ea159 100644 --- a/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc @@ -79,19 +79,19 @@ TEST_F(KernelProxyTest, FileLeak) { MountMem* mount = (MountMem*)kp_.RootMount(); ScopedMountNode root; - EXPECT_EQ(0, mount->Open(Path("/"), O_RDONLY, &root)); - EXPECT_EQ(0, root->ChildCount()); + ASSERT_EQ(0, mount->Open(Path("/"), O_RDONLY, &root)); + ASSERT_EQ(0, root->ChildCount()); for (int file_num = 0; file_num < 4096; file_num++) { sprintf(filename, "/foo%i.tmp", file_num++); FILE* f = fopen(filename, "w"); - EXPECT_NE((FILE*)0, f); - EXPECT_EQ(1, root->ChildCount()); - EXPECT_EQ(buffer_size, fwrite(garbage, 1, buffer_size, f)); + ASSERT_NE((FILE*)NULL, f); + ASSERT_EQ(1, root->ChildCount()); + ASSERT_EQ(buffer_size, fwrite(garbage, 1, buffer_size, f)); fclose(f); - EXPECT_EQ(0, remove(filename)); + ASSERT_EQ(0, remove(filename)); } - EXPECT_EQ(0, root->ChildCount()); + ASSERT_EQ(0, root->ChildCount()); } TEST_F(KernelProxyTest, WorkingDirectory) { @@ -157,12 +157,12 @@ TEST_F(KernelProxyTest, MemMountIO) { EXPECT_EQ(ENOENT, errno); // Create bar "/foo/bar" - fd1 = ki_open("/foo/bar", O_RDONLY | O_CREAT); - EXPECT_NE(-1, fd1); + fd1 = ki_open("/foo/bar", O_RDWR | O_CREAT); + ASSERT_NE(-1, fd1); // Open (optionally create) bar "/foo/bar" - fd2 = ki_open("/foo/bar", O_RDONLY | O_CREAT); - EXPECT_NE(-1, fd2); + fd2 = ki_open("/foo/bar", O_RDWR | O_CREAT); + ASSERT_NE(-1, fd2); // Fail to exclusively create bar "/foo/bar" EXPECT_EQ(-1, ki_open("/foo/bar", O_RDONLY | O_CREAT | O_EXCL)); @@ -173,30 +173,27 @@ TEST_F(KernelProxyTest, MemMountIO) { EXPECT_EQ(5, ki_write(fd2, "WORLD", 5)); EXPECT_EQ(5, ki_write(fd1, "HELLO", 5)); - fd3 = ki_open("/foo/bar", O_WRONLY); - EXPECT_NE(-1, fd3); + fd3 = ki_open("/foo/bar", O_RDONLY); + ASSERT_NE(-1, fd3); len = ki_read(fd3, text, sizeof(text)); - if (len > 0) - text[len] = 0; - EXPECT_EQ(5, len); + ASSERT_EQ(5, len); + text[len] = 0; EXPECT_STREQ("HELLO", text); EXPECT_EQ(0, ki_close(fd1)); EXPECT_EQ(0, ki_close(fd2)); fd1 = ki_open("/foo/bar", O_WRONLY | O_APPEND); - EXPECT_NE(-1, fd1); + ASSERT_NE(-1, fd1); EXPECT_EQ(5, ki_write(fd1, "WORLD", 5)); len = ki_read(fd3, text, sizeof(text)); - if (len >= 0) - text[len] = 0; - - EXPECT_EQ(5, len); + ASSERT_EQ(5, len); + text[len] = 0; EXPECT_STREQ("WORLD", text); fd2 = ki_open("/foo/bar", O_RDONLY); - EXPECT_NE(-1, fd2); + ASSERT_NE(-1, fd2); len = ki_read(fd2, text, sizeof(text)); if (len > 0) text[len] = 0; @@ -206,6 +203,7 @@ TEST_F(KernelProxyTest, MemMountIO) { TEST_F(KernelProxyTest, MemMountLseek) { int fd = ki_open("/foo", O_CREAT | O_RDWR); + ASSERT_GT(fd, -1); EXPECT_EQ(9, ki_write(fd, "Some text", 9)); EXPECT_EQ(9, ki_lseek(fd, 0, SEEK_CUR)); @@ -225,10 +223,12 @@ TEST_F(KernelProxyTest, MemMountLseek) { TEST_F(KernelProxyTest, CloseTwice) { int fd = ki_open("/foo", O_CREAT | O_RDWR); + ASSERT_GT(fd, -1); + EXPECT_EQ(9, ki_write(fd, "Some text", 9)); int fd2 = ki_dup(fd); - EXPECT_NE(-1, fd2); + ASSERT_GT(fd2, -1); EXPECT_EQ(0, ki_close(fd)); EXPECT_EQ(0, ki_close(fd2)); @@ -236,32 +236,33 @@ TEST_F(KernelProxyTest, CloseTwice) { TEST_F(KernelProxyTest, MemMountDup) { int fd = ki_open("/foo", O_CREAT | O_RDWR); + ASSERT_GT(fd, -1); int dup_fd = ki_dup(fd); - EXPECT_NE(-1, dup_fd); + ASSERT_NE(-1, dup_fd); - EXPECT_EQ(9, ki_write(fd, "Some text", 9)); - EXPECT_EQ(9, ki_lseek(fd, 0, SEEK_CUR)); - EXPECT_EQ(9, ki_lseek(dup_fd, 0, SEEK_CUR)); + ASSERT_EQ(9, ki_write(fd, "Some text", 9)); + ASSERT_EQ(9, ki_lseek(fd, 0, SEEK_CUR)); + ASSERT_EQ(9, ki_lseek(dup_fd, 0, SEEK_CUR)); int dup2_fd = 123; - EXPECT_EQ(dup2_fd, ki_dup2(fd, dup2_fd)); - EXPECT_EQ(9, ki_lseek(dup2_fd, 0, SEEK_CUR)); + ASSERT_EQ(dup2_fd, ki_dup2(fd, dup2_fd)); + ASSERT_EQ(9, ki_lseek(dup2_fd, 0, SEEK_CUR)); int new_fd = ki_open("/bar", O_CREAT | O_RDWR); - EXPECT_EQ(fd, ki_dup2(new_fd, fd)); + ASSERT_EQ(fd, ki_dup2(new_fd, fd)); // fd, new_fd -> "/bar" // dup_fd, dup2_fd -> "/foo" // We should still be able to write to dup_fd (i.e. it should not be closed). - EXPECT_EQ(4, ki_write(dup_fd, "more", 4)); + ASSERT_EQ(4, ki_write(dup_fd, "more", 4)); - EXPECT_EQ(0, ki_close(dup2_fd)); + ASSERT_EQ(0, ki_close(dup2_fd)); // fd, new_fd -> "/bar" // dup_fd -> "/foo" - EXPECT_EQ(dup_fd, ki_dup2(fd, dup_fd)); + ASSERT_EQ(dup_fd, ki_dup2(fd, dup_fd)); // fd, new_fd, dup_fd -> "/bar" } @@ -404,29 +405,29 @@ class KernelProxyMMapTest : public ::testing::Test { } // namespace TEST_F(KernelProxyMMapTest, MMap) { - EXPECT_EQ(0, ki_umount("/")); - EXPECT_EQ(0, ki_mount("", "/", "mmapfs", 0, NULL)); + ASSERT_EQ(0, ki_umount("/")); + ASSERT_EQ(0, ki_mount("", "/", "mmapfs", 0, NULL)); int fd = ki_open("/file", O_RDWR | O_CREAT); - EXPECT_NE(-1, fd); + ASSERT_NE(-1, fd); void* addr1 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0); - EXPECT_EQ(reinterpret_cast<void*>(0x1000), addr1); - EXPECT_EQ(1, g_MMapCount); + ASSERT_EQ(reinterpret_cast<void*>(0x1000), addr1); + ASSERT_EQ(1, g_MMapCount); void* addr2 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0); - EXPECT_EQ(reinterpret_cast<void*>(0x2000), addr2); - EXPECT_EQ(2, g_MMapCount); + ASSERT_EQ(reinterpret_cast<void*>(0x2000), addr2); + ASSERT_EQ(2, g_MMapCount); void* addr3 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0); - EXPECT_EQ(reinterpret_cast<void*>(0x3000), addr3); - EXPECT_EQ(3, g_MMapCount); + ASSERT_EQ(reinterpret_cast<void*>(0x3000), addr3); + ASSERT_EQ(3, g_MMapCount); ki_close(fd); // We no longer track mmap'd regions, so munmap is a no-op. - EXPECT_EQ(0, ki_munmap(reinterpret_cast<void*>(0x1000), 0x2800)); + ASSERT_EQ(0, ki_munmap(reinterpret_cast<void*>(0x1000), 0x2800)); // We don't track regions, so the mmap count hasn't changed. - EXPECT_EQ(3, g_MMapCount); + ASSERT_EQ(3, g_MMapCount); } namespace { diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc index c7fa6cc..12f4fcf 100644 --- a/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc @@ -233,45 +233,45 @@ TEST_F(MountHtml5FsTest, OpenForCreate) { TEST_F(MountHtml5FsTest, Read) { const char contents[] = "contents"; - EXPECT_TRUE( + ASSERT_TRUE( ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL)); - EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); + ASSERT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); StringMap_t map; ScopedRef<MountHtml5FsForTesting> mnt( new MountHtml5FsForTesting(map, &ppapi_)); ScopedMountNode node; - EXPECT_EQ(0, mnt->Open(Path("/file"), O_RDONLY, &node)); + ASSERT_EQ(0, mnt->Open(Path("/file"), O_RDONLY, &node)); char buffer[10] = {0}; int bytes_read = 0; HandleAttr attr; - EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); - EXPECT_EQ(strlen(contents), bytes_read); - EXPECT_STREQ(contents, buffer); + ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); + ASSERT_EQ(strlen(contents), bytes_read); + ASSERT_STREQ(contents, buffer); // Read nothing past the end of the file. attr.offs = 100; - EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); - EXPECT_EQ(0, bytes_read); + ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); + ASSERT_EQ(0, bytes_read); // Read part of the data. attr.offs = 4; - EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); + ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); ASSERT_EQ(strlen(contents) - 4, bytes_read); buffer[bytes_read] = 0; - EXPECT_STREQ("ents", buffer); + ASSERT_STREQ("ents", buffer); // Writing should fail. int bytes_written = 1; // Set to a non-zero value. attr.offs = 0; - EXPECT_EQ(EACCES, node->Write(attr, &buffer[0], sizeof(buffer), + ASSERT_EQ(EACCES, node->Write(attr, &buffer[0], sizeof(buffer), &bytes_written)); - EXPECT_EQ(0, bytes_written); + ASSERT_EQ(0, bytes_written); // Reading from a directory should fail. - EXPECT_EQ(0, mnt->Open(Path("/dir"), O_RDONLY, &node)); - EXPECT_EQ(EISDIR, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); + ASSERT_EQ(0, mnt->Open(Path("/dir"), O_RDONLY, &node)); + ASSERT_EQ(EISDIR, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); } TEST_F(MountHtml5FsTest, Write) { @@ -343,7 +343,9 @@ TEST_F(MountHtml5FsTest, GetStat) { struct stat statbuf; EXPECT_EQ(0, node->GetStat(&statbuf)); - EXPECT_EQ(S_IFREG | S_IWRITE | S_IREAD, statbuf.st_mode); + EXPECT_EQ(S_IFREG, statbuf.st_mode & S_IFMT); + EXPECT_EQ(S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH, statbuf.st_mode & ~S_IFMT); EXPECT_EQ(strlen(contents), statbuf.st_size); EXPECT_EQ(access_time, statbuf.st_atime); EXPECT_EQ(creation_time, statbuf.st_ctime); @@ -360,7 +362,9 @@ TEST_F(MountHtml5FsTest, GetStat) { // GetStat on a directory... EXPECT_EQ(0, mnt->Open(Path("/dir"), O_RDONLY, &node)); EXPECT_EQ(0, node->GetStat(&statbuf)); - EXPECT_EQ(S_IFDIR | S_IWRITE | S_IREAD, statbuf.st_mode); + EXPECT_EQ(S_IFDIR, statbuf.st_mode & S_IFMT); + EXPECT_EQ(S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH, statbuf.st_mode & ~S_IFMT); EXPECT_EQ(0, statbuf.st_size); EXPECT_EQ(access_time, statbuf.st_atime); EXPECT_EQ(creation_time, statbuf.st_ctime); diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc index f9aba9b..8ec27db 100644 --- a/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc @@ -28,7 +28,6 @@ using ::testing::Return; using ::testing::SetArgPointee; using ::testing::StrEq; - class MountHttpMock : public MountHttp { public: MountHttpMock(StringMap_t map, PepperInterfaceMock* ppapi) { @@ -164,10 +163,10 @@ TEST_F(MountHttpTest, ParseManifest) { EXPECT_FALSE(bar->GetStat(&sbar)); EXPECT_EQ(123, sfoo.st_size); - EXPECT_EQ(S_IFREG | S_IREAD, sfoo.st_mode); + EXPECT_EQ(S_IFREG | S_IRALL, sfoo.st_mode); EXPECT_EQ(234, sbar.st_size); - EXPECT_EQ(S_IFREG | S_IREAD | S_IWRITE, sbar.st_mode); + EXPECT_EQ(S_IFREG | S_IRALL | S_IWALL, sbar.st_mode); } diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc index f2d638d..666b104 100644 --- a/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc @@ -67,11 +67,11 @@ TEST(MountNodeTest, File) { size_t result_size = 0; int result_bytes = 0; - EXPECT_EQ(0, file.Init(S_IREAD | S_IWRITE)); + EXPECT_EQ(0, file.Init(0)); // Test properties EXPECT_EQ(0, file.GetLinks()); - EXPECT_EQ(S_IREAD | S_IWRITE, file.GetMode()); + EXPECT_EQ(S_IRALL | S_IWALL, file.GetMode()); EXPECT_EQ(S_IFREG, file.GetType()); EXPECT_FALSE(file.IsaDir()); EXPECT_TRUE(file.IsaFile()); @@ -166,20 +166,20 @@ TEST(MountNodeTest, Fcntl) { ScopedMount mnt(new MockMount()); ScopedMountNode file(node); KernelHandle handle(mnt, file); - ASSERT_EQ(0, handle.Init(O_CREAT|O_APPEND)); + ASSERT_EQ(0, handle.Init(O_CREAT | O_APPEND)); // Test F_GETFL - ASSERT_EQ(0, node->Init(S_IREAD | S_IWRITE)); + ASSERT_EQ(0, node->Init(0)); int flags = 0; ASSERT_EQ(0, handle.Fcntl(F_GETFL, &flags)); - ASSERT_EQ(O_CREAT|O_APPEND, flags); + ASSERT_EQ(O_CREAT | O_APPEND, flags); // Test F_SETFL // Test adding of O_NONBLOCK - flags = O_NONBLOCK|O_APPEND; + flags = O_NONBLOCK | O_APPEND; ASSERT_EQ(0, handle.Fcntl(F_SETFL, NULL, flags)); ASSERT_EQ(0, handle.Fcntl(F_GETFL, &flags)); - ASSERT_EQ(O_CREAT|O_APPEND|O_NONBLOCK, flags); + ASSERT_EQ(O_CREAT | O_APPEND | O_NONBLOCK, flags); // Clearing of O_APPEND should generate EPERM; flags = O_NONBLOCK; @@ -193,12 +193,12 @@ TEST(MountNodeTest, Directory) { size_t result_size = 0; int result_bytes = 0; - root.Init(S_IREAD | S_IWRITE); + root.Init(0); // Test properties EXPECT_EQ(0, root.GetLinks()); // Directories are always executable. - EXPECT_EQ(S_IREAD | S_IWRITE | S_IEXEC, root.GetMode()); + EXPECT_EQ(S_IRALL | S_IWALL | S_IXALL, root.GetMode()); EXPECT_EQ(S_IFDIR, root.GetType()); EXPECT_TRUE(root.IsaDir()); EXPECT_FALSE(root.IsaFile()); @@ -215,7 +215,7 @@ TEST(MountNodeTest, Directory) { // Test directory operations MockNode* raw_file = new MockNode; - EXPECT_EQ(0, raw_file->Init(S_IREAD | S_IWRITE)); + EXPECT_EQ(0, raw_file->Init(0)); ScopedMountNode file(raw_file); EXPECT_EQ(0, root.RefCount()); @@ -278,3 +278,41 @@ TEST(MountNodeTest, Directory) { file.reset(); EXPECT_EQ(1, s_AllocNum); } + +TEST(MountNodeTest, OpenMode) { + MockNode* node = new MockNode(); + ScopedMount mnt(new MockMount()); + ScopedMountNode file(node); + + const char write_buf[] = "hello world"; + char read_buf[10]; + int byte_count = 0; + + // Write some data to the file + { + KernelHandle handle(mnt, file); + ASSERT_EQ(0, handle.Init(O_CREAT | O_WRONLY)); + ASSERT_EQ(0, handle.Write(write_buf, strlen(write_buf), &byte_count)); + ASSERT_EQ(byte_count, strlen(write_buf)); + } + + // Reading from the O_WRONLY handle should be impossible + { + byte_count = 0; + KernelHandle handle(mnt, file); + ASSERT_EQ(0, handle.Init(O_WRONLY)); + ASSERT_EQ(EACCES, handle.Read(read_buf, 10, &byte_count)); + ASSERT_EQ(0, handle.Write(write_buf, strlen(write_buf), &byte_count)); + ASSERT_EQ(byte_count, strlen(write_buf)); + } + + // Writing to a O_RDONLY handle should fail + { + byte_count = 0; + KernelHandle handle(mnt, file); + ASSERT_EQ(0, handle.Init(O_RDONLY)); + ASSERT_EQ(EACCES, handle.Write(write_buf, strlen(write_buf), &byte_count)); + ASSERT_EQ(0, handle.Read(read_buf, sizeof(read_buf), &byte_count)); + ASSERT_EQ(byte_count, sizeof(read_buf)); + } +} diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_test.cc index f256624..eb670b4 100644 --- a/native_client_sdk/src/tests/nacl_io_test/mount_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_test.cc @@ -55,9 +55,7 @@ TEST(MountTest, Sanity) { // Create a file EXPECT_EQ(0, mnt.Open(Path("/foo"), O_RDWR | O_CREAT, &file)); - EXPECT_NE(NULL_NODE, file.get()); - if (file == NULL) - return; + ASSERT_NE(NULL_NODE, file.get()); // We now have a directory and a file. The file has a two references // one returned to the test, one for the name->inode map. @@ -75,14 +73,12 @@ TEST(MountTest, Sanity) { // Open the root directory, should not create a new file EXPECT_EQ(0, mnt.Open(Path("/"), O_RDONLY, &root)); EXPECT_EQ(2, mnt.num_nodes()); - EXPECT_NE(NULL_NODE, root.get()); - if (NULL != root) { - struct dirent dirs[4]; - int len; - EXPECT_EQ(0, root->GetDents(0, dirs, sizeof(dirs), &len)); - // 3 == "foo", ".", ".." - EXPECT_EQ(3 * sizeof(struct dirent), len); - } + ASSERT_NE(NULL_NODE, root.get()); + struct dirent dirs[4]; + int len; + EXPECT_EQ(0, root->GetDents(0, dirs, sizeof(dirs), &len)); + // 3 == "foo", ".", ".." + EXPECT_EQ(3 * sizeof(struct dirent), len); // Fail to re-create the same file EXPECT_EQ(EEXIST, @@ -136,9 +132,7 @@ TEST(MountTest, Sanity) { // Create a file (exclusively) EXPECT_EQ(0, mnt.Open(Path("/foo/bar"), O_RDWR | O_CREAT | O_EXCL, &file)); - EXPECT_NE(NULL_NODE, file.get()); - if (NULL == file) - return; + ASSERT_NE(NULL_NODE, file.get()); EXPECT_EQ(2, file->RefCount()); EXPECT_EQ(3, mnt.num_nodes()); |