Commit b5c5b46e authored by mgiuca@chromium.org's avatar mgiuca@chromium.org

[NaCl SDK] nacl_io: Added support for access() syscall.

BUG=246470

Review URL: https://chromiumcodereview.appspot.com/15800004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207754 0039d316-1c4b-4281-b951-d872f2087c98
parent ee77da87
...@@ -558,10 +558,25 @@ int KernelProxy::fchmod(int fd, int mode) { ...@@ -558,10 +558,25 @@ int KernelProxy::fchmod(int fd, int mode) {
} }
int KernelProxy::access(const char* path, int amode) { int KernelProxy::access(const char* path, int amode) {
errno = EINVAL; Path rel;
return -1;
Mount* mnt;
Error error = AcquireMountAndPath(path, &mnt, &rel);
if (error) {
errno = error;
return -1;
}
error = mnt->Access(rel, amode);
ReleaseMount(mnt);
if (error) {
errno = error;
return -1;
}
return 0;
} }
// TODO(noelallen): Needs implementation.
int KernelProxy::link(const char* oldpath, const char* newpath) { int KernelProxy::link(const char* oldpath, const char* newpath) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
......
...@@ -52,6 +52,10 @@ class Mount : public RefObject { ...@@ -52,6 +52,10 @@ class Mount : public RefObject {
// All paths in functions below are expected to containing a leading "/". // All paths in functions below are expected to containing a leading "/".
// Test whether a file or directory at a given path can be accessed.
// Returns 0 on success, or an appropriate errno value on failure.
virtual Error Access(const Path& path, int a_mode) = 0;
// Open a node at |path| with the specified open flags. The resulting // Open a node at |path| with the specified open flags. The resulting
// MountNode is created with a ref count of 1. // MountNode is created with a ref count of 1.
// Assumes that |out_node| is non-NULL. // Assumes that |out_node| is non-NULL.
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "nacl_io/kernel_wrap_real.h" #include "nacl_io/kernel_wrap_real.h"
#include "nacl_io/mount_dev.h" #include "nacl_io/mount_dev.h"
#include "nacl_io/mount_node.h" #include "nacl_io/mount_node.h"
...@@ -269,6 +270,22 @@ Error UrandomNode::Write(size_t offs, ...@@ -269,6 +270,22 @@ Error UrandomNode::Write(size_t offs,
} // namespace } // namespace
Error MountDev::Access(const Path& path, int a_mode) {
MountNode* node = NULL;
AutoLock lock(&lock_);
int error = root_->FindChild(path.Join(), &node);
if (error)
return error;
// Don't allow execute access.
if (a_mode & X_OK)
return EACCES;
return 0;
}
Error MountDev::Open(const Path& path, int mode, MountNode** out_node) { Error MountDev::Open(const Path& path, int mode, MountNode** out_node) {
*out_node = NULL; *out_node = NULL;
......
...@@ -11,6 +11,7 @@ class MountNode; ...@@ -11,6 +11,7 @@ class MountNode;
class MountDev : public Mount { class MountDev : public Mount {
public: public:
virtual Error Access(const Path& path, int a_mode);
virtual Error Open(const Path& path, int mode, MountNode** out_node); virtual Error Open(const Path& path, int mode, MountNode** out_node);
virtual Error Unlink(const Path& path); virtual Error Unlink(const Path& path);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "nacl_io/mount_html5fs.h" #include "nacl_io/mount_html5fs.h"
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <ppapi/c/pp_completion_callback.h> #include <ppapi/c/pp_completion_callback.h>
#include <ppapi/c/pp_errors.h> #include <ppapi/c/pp_errors.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -24,6 +25,17 @@ int64_t strtoull(const char* nptr, char** endptr, int base) { ...@@ -24,6 +25,17 @@ int64_t strtoull(const char* nptr, char** endptr, int base) {
} // namespace } // namespace
Error MountHtml5Fs::Access(const Path& path, int a_mode) {
// a_mode is unused, since all files are readable, writable and executable.
MountNode* node;
Error error = Open(path, O_RDONLY, &node);
if (error)
return error;
node->Release();
return 0;
}
Error MountHtml5Fs::Open(const Path& path, int mode, MountNode** out_node) { Error MountHtml5Fs::Open(const Path& path, int mode, MountNode** out_node) {
*out_node = NULL; *out_node = NULL;
......
...@@ -13,6 +13,7 @@ class MountNode; ...@@ -13,6 +13,7 @@ class MountNode;
class MountHtml5Fs : public Mount { class MountHtml5Fs : public Mount {
public: public:
virtual Error Access(const Path& path, int a_mode);
virtual Error Open(const Path& path, int mode, MountNode** out_node); virtual Error Open(const Path& path, int mode, MountNode** out_node);
virtual Error Unlink(const Path& path); virtual Error Unlink(const Path& path);
virtual Error Mkdir(const Path& path, int permissions); virtual Error Mkdir(const Path& path, int permissions);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h>
#include <vector> #include <vector>
...@@ -54,6 +55,33 @@ std::string NormalizeHeaderKey(const std::string& s) { ...@@ -54,6 +55,33 @@ std::string NormalizeHeaderKey(const std::string& s) {
return result; return result;
} }
Error MountHttp::Access(const Path& path, int a_mode) {
assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/');
NodeMap_t::iterator iter = node_cache_.find(path.Join());
if (iter == node_cache_.end()) {
// If we can't find the node in the cache, fetch it
std::string url = MakeUrl(path);
MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_);
Error error = node->Init(O_RDONLY);
if (error) {
node->Release();
return error;
}
error = node->GetStat(NULL);
node->Release();
if (error)
return error;
}
// Don't allow write or execute access.
if (a_mode & (W_OK | X_OK))
return EACCES;
return 0;
}
Error MountHttp::Open(const Path& path, int mode, MountNode** out_node) { Error MountHttp::Open(const Path& path, int mode, MountNode** out_node) {
*out_node = NULL; *out_node = NULL;
...@@ -66,9 +94,7 @@ Error MountHttp::Open(const Path& path, int mode, MountNode** out_node) { ...@@ -66,9 +94,7 @@ Error MountHttp::Open(const Path& path, int mode, MountNode** out_node) {
} }
// 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 = url_root_ + (path.IsAbsolute() ? path.Range(1, path.Size()) std::string url = MakeUrl(path);
: path.Join());
MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_);
Error error = node->Init(mode); Error error = node->Init(mode);
if (error) { if (error) {
...@@ -337,10 +363,7 @@ Error MountHttp::ParseManifest(char* text) { ...@@ -337,10 +363,7 @@ Error MountHttp::ParseManifest(char* text) {
} }
Path path(name); Path path(name);
std::string url = std::string url = MakeUrl(path);
url_root_ +
(path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join());
MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_);
Error error = node->Init(mode); Error error = node->Init(mode);
if (error) { if (error) {
...@@ -402,3 +425,8 @@ Error MountHttp::LoadManifest(const std::string& manifest_name, ...@@ -402,3 +425,8 @@ Error MountHttp::LoadManifest(const std::string& manifest_name,
*out_manifest = text; *out_manifest = text;
return 0; return 0;
} }
std::string MountHttp::MakeUrl(const Path& path) {
return url_root_ +
(path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join());
}
...@@ -21,6 +21,7 @@ class MountHttp : public Mount { ...@@ -21,6 +21,7 @@ class MountHttp : public Mount {
public: public:
typedef std::map<std::string, MountNode*> NodeMap_t; typedef std::map<std::string, MountNode*> NodeMap_t;
virtual Error Access(const Path& path, int a_mode);
virtual Error Open(const Path& path, int mode, MountNode** out_node); virtual Error Open(const Path& path, int mode, MountNode** out_node);
virtual Error Unlink(const Path& path); virtual Error Unlink(const Path& path);
virtual Error Mkdir(const Path& path, int permissions); virtual Error Mkdir(const Path& path, int permissions);
...@@ -41,6 +42,10 @@ class MountHttp : public Mount { ...@@ -41,6 +42,10 @@ class MountHttp : public Mount {
Error ParseManifest(char *text); Error ParseManifest(char *text);
private: private:
// Gets the URL to fetch for |path|.
// |path| is relative to the mount point for the HTTP filesystem.
std::string MakeUrl(const Path& path);
std::string url_root_; std::string url_root_;
StringMap_t headers_; StringMap_t headers_;
NodeMap_t node_cache_; NodeMap_t node_cache_;
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#include <string> #include <string>
#include "nacl_io/mount.h" #include "nacl_io/mount.h"
...@@ -76,6 +78,24 @@ Error MountMem::FindNode(const Path& path, int type, MountNode** out_node) { ...@@ -76,6 +78,24 @@ Error MountMem::FindNode(const Path& path, int type, MountNode** out_node) {
return 0; return 0;
} }
Error MountMem::Access(const Path& path, int a_mode) {
AutoLock lock(&lock_);
MountNode* node = NULL;
Error error = FindNode(path, 0, &node);
if (error)
return error;
int obj_mode = node->GetMode();
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 0;
}
Error MountMem::Open(const Path& path, int mode, MountNode** out_node) { Error MountMem::Open(const Path& path, int mode, MountNode** out_node) {
AutoLock lock(&lock_); AutoLock lock(&lock_);
MountNode* node = NULL; MountNode* node = NULL;
......
...@@ -29,6 +29,7 @@ class MountMem : public Mount { ...@@ -29,6 +29,7 @@ class MountMem : public Mount {
virtual Error FindNode(const Path& path, int type, MountNode** out_node); virtual Error FindNode(const Path& path, int type, MountNode** out_node);
public: public:
virtual Error Access(const Path& path, int a_mode);
virtual Error Open(const Path& path, int mode, MountNode** out_node); virtual Error Open(const Path& path, int mode, MountNode** out_node);
virtual Error Unlink(const Path& path); virtual Error Unlink(const Path& path);
virtual Error Mkdir(const Path& path, int perm); virtual Error Mkdir(const Path& path, int perm);
......
...@@ -108,6 +108,11 @@ Error MountPassthrough::Init(int dev, ...@@ -108,6 +108,11 @@ Error MountPassthrough::Init(int dev,
void MountPassthrough::Destroy() {} void MountPassthrough::Destroy() {}
Error MountPassthrough::Access(const Path& path, int a_mode) {
// There is no underlying 'access' syscall in NaCl. It just returns ENOSYS.
return ENOSYS;
}
Error MountPassthrough::Open(const Path& path, int mode, MountNode** out_node) { Error MountPassthrough::Open(const Path& path, int mode, MountNode** out_node) {
*out_node = NULL; *out_node = NULL;
......
...@@ -15,6 +15,7 @@ class MountPassthrough : public Mount { ...@@ -15,6 +15,7 @@ class MountPassthrough : public Mount {
virtual void Destroy(); virtual void Destroy();
public: public:
virtual Error Access(const Path& path, int a_mode);
virtual Error Open(const Path& path, int mode, MountNode** out_node); virtual Error Open(const Path& path, int mode, MountNode** out_node);
virtual Error OpenResource(const Path& path, MountNode** out_node); virtual Error OpenResource(const Path& path, MountNode** out_node);
virtual Error Unlink(const Path& path); virtual Error Unlink(const Path& path);
......
...@@ -30,6 +30,7 @@ class MountRefMock : public Mount { ...@@ -30,6 +30,7 @@ class MountRefMock : public Mount {
~MountRefMock() { (*mount_count)--; } ~MountRefMock() { (*mount_count)--; }
public: public:
Error Access(const Path& path, int a_mode) { return ENOSYS; }
Error Open(const Path& path, int mode, MountNode** out_node) { Error Open(const Path& path, int mode, MountNode** out_node) {
*out_node = NULL; *out_node = NULL;
return ENOSYS; return ENOSYS;
......
...@@ -282,6 +282,8 @@ class MountNodeMockMMap : public MountNode { ...@@ -282,6 +282,8 @@ class MountNodeMockMMap : public MountNode {
class MountMockMMap : public Mount { class MountMockMMap : public Mount {
public: public:
virtual Error Access(const Path& path, int a_mode) { return 0; }
virtual Error Open(const Path& path, int mode, MountNode** out_node) { virtual Error Open(const Path& path, int mode, MountNode** out_node) {
MountNodeMockMMap* node = new MountNodeMockMMap(this); MountNodeMockMMap* node = new MountNodeMockMMap(this);
*out_node = node; *out_node = node;
......
...@@ -319,6 +319,77 @@ TEST_F(MountHtml5FsTest, FilesystemType) { ...@@ -319,6 +319,77 @@ TEST_F(MountHtml5FsTest, FilesystemType) {
MountHtml5FsMock mnt(map, ppapi_); MountHtml5FsMock mnt(map, ppapi_);
} }
TEST_F(MountHtml5FsTest, Access) {
const char path[] = "/foo";
const PP_Resource fileref_resource = 235;
const PP_Resource fileio_resource = 236;
// These are the default values.
SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0);
FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface();
FileIoInterfaceMock* fileio = ppapi_->GetFileIoInterface();
EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0])))
.WillOnce(Return(fileref_resource));
PP_FileInfo info;
memset(&info, 0, sizeof(PP_FileInfo));
info.type = PP_FILETYPE_REGULAR;
EXPECT_CALL(*fileref, Query(fileref_resource, _, _))
.WillOnce(DoAll(SetArgPointee<1>(info),
Return(int32_t(PP_OK))));
EXPECT_CALL(*fileio, Create(instance_)).WillOnce(Return(fileio_resource));
int32_t open_flags = PP_FILEOPENFLAG_READ;
EXPECT_CALL(*fileio,
Open(fileio_resource, fileref_resource, open_flags, _))
.WillOnce(Return(int32_t(PP_OK)));
EXPECT_CALL(*fileio, Close(fileio_resource));
EXPECT_CALL(*fileio, Flush(fileio_resource, _));
EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource));
EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource));
StringMap_t map;
MountHtml5FsMock mnt(map, ppapi_);
ASSERT_EQ(0, mnt.Access(Path(path), R_OK | W_OK | X_OK));
}
TEST_F(MountHtml5FsTest, AccessFileNotFound) {
const char path[] = "/foo";
const PP_Resource fileref_resource = 235;
const PP_Resource fileio_resource = 236;
// These are the default values.
SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0);
FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface();
FileIoInterfaceMock* fileio = ppapi_->GetFileIoInterface();
// Report the file as missing.
EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0])))
.WillOnce(Return(fileref_resource));
PP_FileInfo info;
memset(&info, 0, sizeof(PP_FileInfo));
info.type = PP_FILETYPE_REGULAR;
EXPECT_CALL(*fileref, Query(fileref_resource, _, _))
.WillOnce(DoAll(SetArgPointee<1>(info),
Return(int32_t(PP_ERROR_FILENOTFOUND))));
EXPECT_CALL(*fileio, Create(instance_)).WillOnce(Return(fileio_resource));
int32_t open_flags = PP_FILEOPENFLAG_READ;
EXPECT_CALL(*fileio,
Open(fileio_resource, fileref_resource, open_flags, _))
.WillOnce(Return(int32_t(PP_ERROR_FILENOTFOUND)));
EXPECT_CALL(*fileio, Close(fileio_resource));
EXPECT_CALL(*fileio, Flush(fileio_resource, _));
EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource));
EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource));
StringMap_t map;
MountHtml5FsMock mnt(map, ppapi_);
ASSERT_EQ(ENOENT, mnt.Access(Path(path), F_OK));
}
TEST_F(MountHtml5FsTest, Mkdir) { TEST_F(MountHtml5FsTest, Mkdir) {
const char path[] = "/foo"; const char path[] = "/foo";
const PP_Resource fileref_resource = 235; const PP_Resource fileref_resource = 235;
......
...@@ -386,6 +386,36 @@ TEST_F(MountHttpNodeTest, GetStat) { ...@@ -386,6 +386,36 @@ TEST_F(MountHttpNodeTest, GetStat) {
EXPECT_EQ(42, stat.st_size); EXPECT_EQ(42, stat.st_size);
} }
TEST_F(MountHttpNodeTest, Access) {
StringMap_t smap;
smap["cache_content"] = "false";
SetMountArgs(StringMap_t());
ExpectOpen("HEAD");
ExpectHeaders("");
SetResponse(200, "");
ASSERT_EQ(0, mnt_->Access(Path(path_), R_OK));
}
TEST_F(MountHttpNodeTest, AccessWrite) {
StringMap_t smap;
smap["cache_content"] = "false";
SetMountArgs(StringMap_t());
ExpectOpen("HEAD");
ExpectHeaders("");
SetResponse(200, "");
ASSERT_EQ(EACCES, mnt_->Access(Path(path_), W_OK));
}
TEST_F(MountHttpNodeTest, AccessNotFound) {
StringMap_t smap;
smap["cache_content"] = "false";
SetMountArgs(StringMap_t());
ExpectOpen("HEAD");
ExpectHeaders("");
SetResponseExpectFail(404, "");
ASSERT_EQ(ENOENT, mnt_->Access(Path(path_), R_OK));
}
TEST_F(MountHttpNodeTest, ReadCached) { TEST_F(MountHttpNodeTest, ReadCached) {
size_t result_size = 0; size_t result_size = 0;
int result_bytes = 0; int result_bytes = 0;
......
...@@ -55,6 +55,7 @@ TEST(MountTest, Sanity) { ...@@ -55,6 +55,7 @@ TEST(MountTest, Sanity) {
EXPECT_EQ(1, mnt->num_nodes()); EXPECT_EQ(1, mnt->num_nodes());
// Fail to open non existent file // Fail to open non existent file
EXPECT_EQ(ENOENT, mnt->Access(Path("/foo"), R_OK | W_OK));
EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &result_node)); EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &result_node));
EXPECT_EQ(NULL, result_node); EXPECT_EQ(NULL, result_node);
...@@ -65,7 +66,12 @@ TEST(MountTest, Sanity) { ...@@ -65,7 +66,12 @@ TEST(MountTest, Sanity) {
return; return;
EXPECT_EQ(2, file->RefCount()); EXPECT_EQ(2, file->RefCount());
EXPECT_EQ(2, mnt->num_nodes()); EXPECT_EQ(2, mnt->num_nodes());
EXPECT_EQ(0, mnt->Access(Path("/foo"), R_OK | W_OK));
EXPECT_EQ(EACCES, mnt->Access(Path("/foo"), X_OK));
// Write access should be allowed on the root directory.
EXPECT_EQ(0, mnt->Access(Path("/"), R_OK | W_OK));
EXPECT_EQ(EACCES, mnt->Access(Path("/"), X_OK));
// Open the root directory for write should fail. // Open the root directory for write should fail.
EXPECT_EQ(EISDIR, mnt->Open(Path("/"), O_RDWR, &root)); EXPECT_EQ(EISDIR, mnt->Open(Path("/"), O_RDWR, &root));
...@@ -136,6 +142,7 @@ TEST(MountTest, Sanity) { ...@@ -136,6 +142,7 @@ TEST(MountTest, Sanity) {
EXPECT_EQ(1, mnt->num_nodes()); EXPECT_EQ(1, mnt->num_nodes());
// Verify the directory is gone // Verify the directory is gone
EXPECT_EQ(ENOENT, mnt->Access(Path("/foo"), F_OK));
EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &file)); EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &file));
EXPECT_EQ(NULL_NODE, file); EXPECT_EQ(NULL_NODE, file);
} }
...@@ -160,11 +167,19 @@ TEST(MountTest, MemMountRemove) { ...@@ -160,11 +167,19 @@ TEST(MountTest, MemMountRemove) {
EXPECT_EQ(NULL_NODE, result_node); EXPECT_EQ(NULL_NODE, result_node);
} }
TEST(MountTest, DevAccess) {
// Should not be able to open non-existent file.
MountDevMock* mnt = new MountDevMock();
ASSERT_EQ(ENOENT, mnt->Access(Path("/foo"), F_OK));
}
TEST(MountTest, DevNull) { TEST(MountTest, DevNull) {
MountDevMock* mnt = new MountDevMock(); MountDevMock* mnt = new MountDevMock();
MountNode* dev_null = NULL; MountNode* dev_null = NULL;
int result_bytes = 0; int result_bytes = 0;
ASSERT_EQ(0, mnt->Access(Path("/null"), R_OK | W_OK));
ASSERT_EQ(EACCES, mnt->Access(Path("/null"), X_OK));
ASSERT_EQ(0, mnt->Open(Path("/null"), O_RDWR, &dev_null)); ASSERT_EQ(0, mnt->Open(Path("/null"), O_RDWR, &dev_null));
ASSERT_NE(NULL_NODE, dev_null); ASSERT_NE(NULL_NODE, dev_null);
...@@ -186,6 +201,8 @@ TEST(MountTest, DevZero) { ...@@ -186,6 +201,8 @@ TEST(MountTest, DevZero) {
MountNode* dev_zero = NULL; MountNode* dev_zero = NULL;
int result_bytes = 0; int result_bytes = 0;
ASSERT_EQ(0, mnt->Access(Path("/zero"), R_OK | W_OK));
ASSERT_EQ(EACCES, mnt->Access(Path("/zero"), X_OK));
ASSERT_EQ(0, mnt->Open(Path("/zero"), O_RDWR, &dev_zero)); ASSERT_EQ(0, mnt->Open(Path("/zero"), O_RDWR, &dev_zero));
ASSERT_NE(NULL_NODE, dev_zero); ASSERT_NE(NULL_NODE, dev_zero);
...@@ -213,6 +230,8 @@ TEST(MountTest, DevUrandom) { ...@@ -213,6 +230,8 @@ TEST(MountTest, DevUrandom) {
MountNode* dev_urandom = NULL; MountNode* dev_urandom = NULL;
int result_bytes = 0; int result_bytes = 0;
ASSERT_EQ(0, mnt->Access(Path("/urandom"), R_OK | W_OK));
ASSERT_EQ(EACCES, mnt->Access(Path("/urandom"), X_OK));
ASSERT_EQ(0, mnt->Open(Path("/urandom"), O_RDWR, &dev_urandom)); ASSERT_EQ(0, mnt->Open(Path("/urandom"), O_RDWR, &dev_urandom));
ASSERT_NE(NULL_NODE, dev_urandom); ASSERT_NE(NULL_NODE, dev_urandom);
......
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