Commit a4ef3a9c authored by Ben Smith's avatar Ben Smith

[NaCl SDK] nacl_io: Add chmod/fchmod

This change includes implementation for MemFs and FuseFs.

I've also reordered the fuse_operations struct to match the order used in the
real FUSE implementation.

BUG=414793
R=sbc@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#296588}
parent 009b55e7
......@@ -69,6 +69,12 @@ Error DirNode::GetDents(size_t offs,
return cache_.GetDents(offs, pdir, size, out_bytes);
}
Error DirNode::Fchmod(mode_t mode) {
AUTO_LOCK(node_lock_);
SetMode(mode);
return 0;
}
Error DirNode::AddChild(const std::string& name, const ScopedNode& node) {
AUTO_LOCK(node_lock_);
......
......@@ -43,6 +43,7 @@ class DirNode : public Node {
const void* buf,
size_t count,
int* out_bytes);
virtual Error Fchmod(mode_t mode);
// Adds a finds or adds a directory entry as an INO, updating the refcount
virtual Error AddChild(const std::string& name, const ScopedNode& node);
......
......@@ -87,35 +87,35 @@ struct fuse_operations {
unsigned int flag_nopath : 1;
unsigned int flag_reserved : 31;
// Called when a filesystem of this type is initialized.
void* (*init)(struct fuse_conn_info* conn);
// Called when a filesystem of this type is unmounted.
void (*destroy)(void*);
// Called by access()
int (*access)(const char* path, int mode);
// Called when O_CREAT is passed to open()
int (*create)(const char* path, mode_t mode, struct fuse_file_info*);
// Called by stat()/fstat(). If this function pointer is non-NULL, it is
// called, otherwise fuse_operations.getattr will be called.
int (*fgetattr)(const char* path, struct stat*, struct fuse_file_info*);
// Called by fsync(). The datasync paramater is not currently supported.
int (*fsync)(const char* path, int datasync, struct fuse_file_info*);
// Called by ftruncate()
int (*ftruncate)(const char* path, off_t, struct fuse_file_info*);
// Called by stat()/fstat(), but only when fuse_operations.fgetattr is NULL.
// Also called by open() to determine if the path is a directory or a regular
// file.
int (*getattr)(const char* path, struct stat*);
// Called by mkdir()
int (*mkdir)(const char* path, mode_t);
// Not called currently.
int (*readlink)(const char*, char*, size_t);
// Called when O_CREAT is passed to open(), but only if fuse_operations.create
// is non-NULL.
int (*mknod)(const char* path, mode_t, dev_t);
// Called by mkdir()
int (*mkdir)(const char* path, mode_t);
// Called by unlink()
int (*unlink)(const char* path);
// Called by rmdir()
int (*rmdir)(const char* path);
// Not called currently.
int (*symlink)(const char*, const char*);
// Called by rename()
int (*rename)(const char* path, const char* new_path);
// Not called currently.
int (*link)(const char*, const char*);
// Called by chmod()/fchmod()
int (*chmod)(const char*, mode_t);
// Not called currently.
int (*chown)(const char*, uid_t, gid_t);
// Called by truncate(), as well as open() when O_TRUNC is passed.
int (*truncate)(const char* path, off_t);
// Called by open()
int (*open)(const char* path, struct fuse_file_info*);
// Called by getdents(), which is called by the more standard functions
// opendir()/readdir().
int (*opendir)(const char* path, struct fuse_file_info*);
// Called by read(). Note that FUSE specifies that all reads will fill the
// entire requested buffer. If this function returns less than that, the
// remainder of the buffer is zeroed.
......@@ -124,6 +124,34 @@ struct fuse_operations {
size_t count,
off_t,
struct fuse_file_info*);
// Called by write(). Note that FUSE specifies that a write should always
// return the full count, unless an error occurs.
int (*write)(const char* path,
const char* buf,
size_t count,
off_t,
struct fuse_file_info*);
// Not called currently.
int (*statfs)(const char*, struct statvfs*);
// Not called currently.
int (*flush)(const char*, struct fuse_file_info*);
// Called when the last reference to this node is released. This is only
// called for regular files. For directories, fuse_operations.releasedir is
// called instead.
int (*release)(const char* path, struct fuse_file_info*);
// Called by fsync(). The datasync paramater is not currently supported.
int (*fsync)(const char* path, int datasync, struct fuse_file_info*);
// Not called currently.
int (*setxattr)(const char*, const char*, const char*, size_t, int);
// Not called currently.
int (*getxattr)(const char*, const char*, char*, size_t);
// Not called currently.
int (*listxattr)(const char*, char*, size_t);
// Not called currently.
int (*removexattr)(const char*, const char*);
// Called by getdents(), which is called by the more standard functions
// opendir()/readdir().
int (*opendir)(const char* path, struct fuse_file_info*);
// Called by getdents(), which is called by the more standard function
// readdir().
//
......@@ -176,68 +204,57 @@ struct fuse_operations {
off_t,
struct fuse_file_info*);
// Called when the last reference to this node is released. This is only
// called for regular files. For directories, fuse_operations.releasedir is
// called instead.
int (*release)(const char* path, struct fuse_file_info*);
// Called when the last reference to this node is released. This is only
// called for directories. For regular files, fuse_operations.release is
// called instead.
int (*releasedir)(const char* path, struct fuse_file_info*);
// Called by rename()
int (*rename)(const char* path, const char* new_path);
// Called by rmdir()
int (*rmdir)(const char* path);
// Called by truncate(), as well as open() when O_TRUNC is passed.
int (*truncate)(const char* path, off_t);
// Called by unlink()
int (*unlink)(const char* path);
// Called by write(). Note that FUSE specifies that a write should always
// return the full count, unless an error occurs.
int (*write)(const char* path,
const char* buf,
size_t count,
off_t,
struct fuse_file_info*);
// Not called currently.
int (*fsyncdir)(const char*, int, struct fuse_file_info*);
// Called when a filesystem of this type is initialized.
void* (*init)(struct fuse_conn_info* conn);
// Called when a filesystem of this type is unmounted.
void (*destroy)(void*);
// Called by access()
int (*access)(const char* path, int mode);
// Called when O_CREAT is passed to open()
int (*create)(const char* path, mode_t mode, struct fuse_file_info*);
// Called by ftruncate()
int (*ftruncate)(const char* path, off_t, struct fuse_file_info*);
// Called by stat()/fstat(). If this function pointer is non-NULL, it is
// called, otherwise fuse_operations.getattr will be called.
int (*fgetattr)(const char* path, struct stat*, struct fuse_file_info*);
// Not called currently.
int (*lock)(const char*, struct fuse_file_info*, int cmd, struct flock*);
// Called by utime()/utimes()/futimes()/futimens() etc.
int (*utimens)(const char*, const struct timespec tv[2]);
// The following functions are not currently called by the nacl_io
// implementation of FUSE.
// Not called currently.
int (*bmap)(const char*, size_t blocksize, uint64_t* idx);
int (*chmod)(const char*, mode_t);
int (*chown)(const char*, uid_t, gid_t);
int (*fallocate)(const char*, int, off_t, off_t, struct fuse_file_info*);
int (*flock)(const char*, struct fuse_file_info*, int op);
int (*flush)(const char*, struct fuse_file_info*);
int (*fsyncdir)(const char*, int, struct fuse_file_info*);
int (*getxattr)(const char*, const char*, char*, size_t);
// Not called currently.
int (*ioctl)(const char*,
int cmd,
void* arg,
struct fuse_file_info*,
unsigned int flags,
void* data);
int (*link)(const char*, const char*);
int (*listxattr)(const char*, char*, size_t);
int (*lock)(const char*, struct fuse_file_info*, int cmd, struct flock*);
// Not called currently.
int (*poll)(const char*,
struct fuse_file_info*,
struct fuse_pollhandle* ph,
unsigned* reventsp);
// Not called currently.
int (*write_buf)(const char*,
struct fuse_bufvec* buf,
off_t off,
struct fuse_file_info*);
// Not called currently.
int (*read_buf)(const char*,
struct fuse_bufvec** bufp,
size_t size,
off_t off,
struct fuse_file_info*);
int (*readlink)(const char*, char*, size_t);
int (*removexattr)(const char*, const char*);
int (*setxattr)(const char*, const char*, const char*, size_t, int);
int (*statfs)(const char*, struct statvfs*);
int (*symlink)(const char*, const char*);
int (*write_buf)(const char*,
struct fuse_bufvec* buf,
off_t off,
struct fuse_file_info*);
// Not called currently.
int (*flock)(const char*, struct fuse_file_info*, int op);
// Not called currently.
int (*fallocate)(const char*, int, off_t, off_t, struct fuse_file_info*);
};
#endif // LIBRARIES_NACL_IO_FUSE_H_
......@@ -261,6 +261,20 @@ Error FuseFsNode::Futimens(const struct timespec times[2]) {
return result;
}
Error FuseFsNode::Fchmod(mode_t mode) {
int result;
if (!fuse_ops_->chmod) {
LOG_TRACE("fuse_ops_->chmod is NULL.");
return ENOSYS;
}
result = fuse_ops_->chmod(path_.c_str(), mode);
if (result < 0)
return -result;
return result;
}
Error FuseFsNode::VIoctl(int request, va_list args) {
LOG_ERROR("Ioctl not implemented for fusefs.");
return ENOSYS;
......
......@@ -55,6 +55,7 @@ class FuseFsNode : public Node {
const struct termios* termios_p);
virtual Error GetSize(off_t* out_size);
virtual Error Futimens(const struct timespec times[2]);
virtual Error Fchmod(mode_t mode);
protected:
struct fuse_operations* fuse_ops_;
......
......@@ -799,8 +799,7 @@ int KernelProxy::remove(const char* path) {
return 0;
}
// TODO(noelallen): Needs implementation.
int KernelProxy::fchmod(int fd, int mode) {
int KernelProxy::fchmod(int fd, mode_t mode) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
......@@ -808,6 +807,12 @@ int KernelProxy::fchmod(int fd, int mode) {
return -1;
}
error = handle->node()->Fchmod(mode);
if (error) {
errno = error;
return -1;
}
return 0;
}
......
......@@ -107,7 +107,7 @@ class KernelProxy : protected KernelObject {
virtual ssize_t read(int fd, void* buf, size_t nbyte);
virtual ssize_t write(int fd, const void* buf, size_t nbyte);
virtual int fchmod(int fd, int prot);
virtual int fchmod(int fd, mode_t mode);
virtual int fcntl(int fd, int request, va_list args);
virtual int fstat(int fd, struct stat* buf);
virtual int getdents(int fd, void* buf, unsigned int count);
......
......@@ -122,4 +122,10 @@ Error MemFsNode::Resize(off_t new_length) {
return 0;
}
Error MemFsNode::Fchmod(mode_t mode) {
AUTO_LOCK(node_lock_);
SetMode(mode);
return 0;
}
} // namespace nacl_io
......@@ -27,6 +27,7 @@ class MemFsNode : public Node {
size_t count,
int* out_bytes);
virtual Error FTruncate(off_t size);
virtual Error Fchmod(mode_t mode);
private:
Error Resize(off_t size);
......
......@@ -180,6 +180,10 @@ Error Node::Futimens(const struct timespec times[2]) {
return 0;
}
Error Node::Fchmod(mode_t mode) {
return EINVAL;
}
int Node::GetLinks() {
return stat_.st_nlink;
}
......
......@@ -90,6 +90,7 @@ class Node : public sdk_util::RefObject {
virtual Error Tcsetattr(int optional_actions,
const struct termios* termios_p);
virtual Error Futimens(const struct timespec times[2]);
virtual Error Fchmod(mode_t mode);
virtual int GetLinks();
virtual int GetMode();
......
......@@ -31,18 +31,16 @@ class FuseFsForTesting : public FuseFs {
// Implementation of a simple flat memory filesystem.
struct File {
File() {
memset(&times, 0, sizeof(times));
}
File() : mode(0666) { memset(&times, 0, sizeof(times)); }
std::string name;
std::vector<uint8_t> data;
mode_t mode;
timespec times[2];
};
typedef std::vector<File> Files;
Files g_files;
mode_t last_create_mode = 0666;
bool IsValidPath(const char* path) {
if (path == NULL)
......@@ -81,7 +79,7 @@ int testfs_getattr(const char* path, struct stat* stbuf) {
if (file == NULL)
return -ENOENT;
stbuf->st_mode = S_IFREG | last_create_mode;
stbuf->st_mode = S_IFREG | file->mode;
stbuf->st_size = file->data.size();
stbuf->st_atime = file->times[0].tv_sec;
stbuf->st_atimensec = file->times[0].tv_nsec;
......@@ -119,7 +117,7 @@ int testfs_create(const char* path, mode_t mode, struct fuse_file_info* fi) {
file = &g_files.back();
file->name = &path[1]; // Skip initial /
}
last_create_mode = mode;
file->mode = mode;
return 0;
}
......@@ -180,33 +178,62 @@ int testfs_utimens(const char* path, const struct timespec times[2]) {
return 0;
}
int testfs_chmod(const char* path, mode_t mode) {
File* file = FindFile(path);
if (file == NULL)
return -ENOENT;
file->mode = mode;
return 0;
}
const char hello_world[] = "Hello, World!\n";
fuse_operations g_fuse_operations = {
0, // flag_nopath
0, // flag_reserved
NULL, // init
NULL, // destroy
NULL, // access
testfs_create, // create
NULL, // fgetattr
NULL, // fsync
NULL, // ftruncate
testfs_getattr, // getattr
NULL, // mkdir
NULL, // readlink
NULL, // mknod
NULL, // mkdir
NULL, // unlink
NULL, // rmdir
NULL, // symlink
NULL, // rename
NULL, // link
testfs_chmod, // chmod
NULL, // chown
NULL, // truncate
testfs_open, // open
NULL, // opendir
testfs_read, // read
testfs_readdir, // readdir
testfs_write, // write
NULL, // statfs
NULL, // flush
NULL, // release
NULL, // fsync
NULL, // setxattr
NULL, // getxattr
NULL, // listxattr
NULL, // removexattr
NULL, // opendir
testfs_readdir, // readdir
NULL, // releasedir
NULL, // rename
NULL, // rmdir
NULL, // truncate
NULL, // unlink
testfs_write, // write
NULL, // fsyncdir
NULL, // init
NULL, // destroy
NULL, // access
testfs_create, // create
NULL, // ftruncate
NULL, // fgetattr
NULL, // lock
testfs_utimens, // utimens
NULL, // bmap
NULL, // ioctl
NULL, // poll
NULL, // write_buf
NULL, // read_buf
NULL, // flock
NULL, // fallocate
};
class FuseFsTest : public ::testing::Test {
......@@ -361,6 +388,20 @@ TEST_F(FuseFsTest, Utimens) {
EXPECT_EQ(times[1].tv_nsec, statbuf.st_mtimensec);
}
TEST_F(FuseFsTest, Fchmod) {
struct stat statbuf;
ScopedNode node;
ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
ASSERT_EQ(0, node->GetStat(&statbuf));
EXPECT_EQ(0666, statbuf.st_mode & ~S_IFMT);
ASSERT_EQ(0, node->Fchmod(0777));
ASSERT_EQ(0, node->GetStat(&statbuf));
EXPECT_EQ(0777, statbuf.st_mode & ~S_IFMT);
}
namespace {
class KernelProxyFuseTest : public ::testing::Test {
......
......@@ -127,6 +127,25 @@ TEST(MemFsNodeTest, File) {
EXPECT_EQ(NULL_NODE, result_node.get());
}
TEST(MemFsNodeTest, Fchmod) {
MemFsNodeForTesting file;
ASSERT_EQ(0, file.Init(0));
EXPECT_EQ(S_IRALL | S_IWALL, file.GetMode());
struct stat s;
ASSERT_EQ(0, file.GetStat(&s));
EXPECT_EQ(S_IFREG | S_IRALL | S_IWALL, s.st_mode);
// Change to read-only.
EXPECT_EQ(0, file.Fchmod(S_IRALL));
EXPECT_EQ(S_IRALL, file.GetMode());
ASSERT_EQ(0, file.GetStat(&s));
EXPECT_EQ(S_IFREG | S_IRALL, s.st_mode);
}
TEST(MemFsNodeTest, FTruncate) {
MemFsNodeForTesting file;
off_t result_size = 0;
......@@ -229,6 +248,12 @@ TEST(MemFsNodeTest, Directory) {
EXPECT_EQ(EISDIR, root.Read(attr, buf1, sizeof(buf1), &result_bytes));
EXPECT_EQ(EISDIR, root.Write(attr, buf1, sizeof(buf1), &result_bytes));
// Chmod test
EXPECT_EQ(0, root.Fchmod(S_IRALL | S_IWALL));
EXPECT_EQ(S_IRALL | S_IWALL, root.GetMode());
// Change it back.
EXPECT_EQ(0, root.Fchmod(S_IRALL | S_IWALL | S_IXALL));
// Test directory operations
MemFsNodeForTesting* raw_file = new MemFsNodeForTesting;
EXPECT_EQ(0, raw_file->Init(0));
......
......@@ -26,7 +26,7 @@ class MockKernelProxy : public nacl_io::KernelProxy {
MOCK_METHOD1(dup, int(int));
MOCK_METHOD2(dup2, int(int, int));
MOCK_METHOD1(fchdir, int(int));
MOCK_METHOD2(fchmod, int(int, int));
MOCK_METHOD2(fchmod, int(int, mode_t));
MOCK_METHOD3(fchown, int(int, uid_t, gid_t));
MOCK_METHOD3(fcntl, int(int, int, va_list));
MOCK_METHOD1(fdatasync, int(int));
......
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