1 // Copyright 2013 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.
5 #include "nacl_io/fusefs/fuse_fs.h"
13 #include "nacl_io/getdents_helper.h"
14 #include "nacl_io/kernel_handle.h"
15 #include "sdk_util/macros.h"
22 FillDirInfo(GetDentsHelper* getdents, size_t num_bytes)
23 : getdents(getdents), num_bytes(num_bytes), wrote_offset(false) {}
25 GetDentsHelper* getdents;
32 FuseFs::FuseFs() : fuse_ops_(NULL), fuse_user_data_(NULL) {}
34 Error FuseFs::Init(const FsInitArgs& args) {
35 Error error = Filesystem::Init(args);
39 fuse_ops_ = args.fuse_ops;
40 if (fuse_ops_ == NULL)
43 if (fuse_ops_->init) {
44 struct fuse_conn_info info;
45 fuse_user_data_ = fuse_ops_->init(&info);
51 void FuseFs::Destroy() {
52 if (fuse_ops_ && fuse_ops_->destroy)
53 fuse_ops_->destroy(fuse_user_data_);
56 Error FuseFs::Access(const Path& path, int a_mode) {
57 if (!fuse_ops_->access)
60 int result = fuse_ops_->access(path.Join().c_str(), a_mode);
67 Error FuseFs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
68 std::string path_str = path.Join();
69 const char* path_cstr = path_str.c_str();
72 struct fuse_file_info fi;
73 memset(&fi, 0, sizeof(fi));
74 fi.flags = open_flags;
76 if (open_flags & (O_CREAT | O_EXCL)) {
77 // According to the FUSE docs, open() is not called when O_CREAT or O_EXCL
79 mode_t mode = S_IRALL | S_IWALL;
80 if (fuse_ops_->create) {
81 result = fuse_ops_->create(path_cstr, mode, &fi);
84 } else if (fuse_ops_->mknod) {
85 result = fuse_ops_->mknod(path_cstr, mode, dev_);
92 // First determine if this is a regular file or a directory.
93 if (fuse_ops_->getattr) {
95 result = fuse_ops_->getattr(path_cstr, &statbuf);
99 if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
100 // This is a directory. Don't try to open, just create a new node with
102 ScopedNode node(new DirFuseFsNode(this, fuse_ops_, fi, path_cstr));
103 Error error = node->Init(open_flags);
113 if (open_flags & O_TRUNC) {
114 // According to the FUSE docs, O_TRUNC does two calls: first truncate()
116 if (!fuse_ops_->truncate)
118 result = fuse_ops_->truncate(path_cstr, 0);
123 if (!fuse_ops_->open)
125 result = fuse_ops_->open(path_cstr, &fi);
130 ScopedNode node(new FileFuseFsNode(this, fuse_ops_, fi, path_cstr));
131 Error error = node->Init(open_flags);
139 Error FuseFs::Unlink(const Path& path) {
140 if (!fuse_ops_->unlink)
143 int result = fuse_ops_->unlink(path.Join().c_str());
150 Error FuseFs::Mkdir(const Path& path, int perm) {
151 if (!fuse_ops_->mkdir)
154 int result = fuse_ops_->mkdir(path.Join().c_str(), perm);
161 Error FuseFs::Rmdir(const Path& path) {
162 if (!fuse_ops_->rmdir)
165 int result = fuse_ops_->rmdir(path.Join().c_str());
172 Error FuseFs::Remove(const Path& path) {
174 Error error = Open(path, O_RDONLY, &node);
179 error = node->GetStat(&statbuf);
185 if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
192 Error FuseFs::Rename(const Path& path, const Path& newpath) {
193 if (!fuse_ops_->rename)
196 int result = fuse_ops_->rename(path.Join().c_str(), newpath.Join().c_str());
203 FuseFsNode::FuseFsNode(Filesystem* filesystem,
204 struct fuse_operations* fuse_ops,
205 struct fuse_file_info& info,
206 const std::string& path)
207 : Node(filesystem), fuse_ops_(fuse_ops), info_(info), path_(path) {}
209 bool FuseFsNode::CanOpen(int open_flags) {
211 Error error = GetStat(&statbuf);
215 // GetStat cached the mode in stat_.st_mode. Forward to Node::CanOpen,
216 // which will check this mode against open_flags.
217 return Node::CanOpen(open_flags);
220 Error FuseFsNode::GetStat(struct stat* stat) {
222 if (fuse_ops_->fgetattr) {
223 result = fuse_ops_->fgetattr(path_.c_str(), stat, &info_);
226 } else if (fuse_ops_->getattr) {
227 result = fuse_ops_->getattr(path_.c_str(), stat);
234 // Also update the cached stat values.
239 Error FuseFsNode::VIoctl(int request, va_list args) {
240 // TODO(binji): implement
244 Error FuseFsNode::Tcflush(int queue_selector) {
245 // TODO(binji): use ioctl for this?
249 Error FuseFsNode::Tcgetattr(struct termios* termios_p) {
250 // TODO(binji): use ioctl for this?
254 Error FuseFsNode::Tcsetattr(int optional_actions,
255 const struct termios* termios_p) {
256 // TODO(binji): use ioctl for this?
260 Error FuseFsNode::GetSize(off_t* out_size) {
262 Error error = GetStat(&statbuf);
266 *out_size = stat_.st_size;
270 FileFuseFsNode::FileFuseFsNode(Filesystem* filesystem,
271 struct fuse_operations* fuse_ops,
272 struct fuse_file_info& info,
273 const std::string& path)
274 : FuseFsNode(filesystem, fuse_ops, info, path) {}
276 void FileFuseFsNode::Destroy() {
277 if (!fuse_ops_->release)
279 fuse_ops_->release(path_.c_str(), &info_);
282 Error FileFuseFsNode::FSync() {
283 if (!fuse_ops_->fsync)
287 int result = fuse_ops_->fsync(path_.c_str(), datasync, &info_);
293 Error FileFuseFsNode::FTruncate(off_t length) {
294 if (!fuse_ops_->ftruncate)
297 int result = fuse_ops_->ftruncate(path_.c_str(), length, &info_);
303 Error FileFuseFsNode::Read(const HandleAttr& attr,
307 if (!fuse_ops_->read)
310 char* cbuf = static_cast<char*>(buf);
312 int result = fuse_ops_->read(path_.c_str(), cbuf, count, attr.offs, &info_);
316 // Fuse docs say that a read() call will always completely fill the buffer
317 // (padding with zeroes) unless the direct_io filesystem flag is set.
318 // TODO(binji): support the direct_io flag
319 if (static_cast<size_t>(result) < count)
320 memset(&cbuf[result], 0, count - result);
326 Error FileFuseFsNode::Write(const HandleAttr& attr,
330 if (!fuse_ops_->write)
333 int result = fuse_ops_->write(
334 path_.c_str(), static_cast<const char*>(buf), count, attr.offs, &info_);
338 // Fuse docs say that a write() call will always write the entire buffer
339 // unless the direct_io filesystem flag is set.
340 // TODO(binji): What should we do if the user breaks this contract? Warn?
341 // TODO(binji): support the direct_io flag
346 DirFuseFsNode::DirFuseFsNode(Filesystem* filesystem,
347 struct fuse_operations* fuse_ops,
348 struct fuse_file_info& info,
349 const std::string& path)
350 : FuseFsNode(filesystem, fuse_ops, info, path) {}
352 void DirFuseFsNode::Destroy() {
353 if (!fuse_ops_->releasedir)
355 fuse_ops_->releasedir(path_.c_str(), &info_);
358 Error DirFuseFsNode::FSync() {
359 if (!fuse_ops_->fsyncdir)
363 int result = fuse_ops_->fsyncdir(path_.c_str(), datasync, &info_);
369 Error DirFuseFsNode::GetDents(size_t offs,
373 if (!fuse_ops_->readdir)
376 bool opened_dir = false;
379 // Opendir is not necessary, only readdir. Call it anyway, if it is defined.
380 if (fuse_ops_->opendir) {
381 result = fuse_ops_->opendir(path_.c_str(), &info_);
389 GetDentsHelper getdents;
390 FillDirInfo fill_info(&getdents, count);
391 result = fuse_ops_->readdir(
392 path_.c_str(), &fill_info, &DirFuseFsNode::FillDirCallback, offs, &info_);
396 // If the fill function ever wrote an entry with |offs| != 0, then assume it
397 // was not given the full list of entries. In that case, GetDentsHelper's
398 // buffers start with the entry at offset |offs|, so the call to
399 // GetDentsHelpers::GetDents should use an offset of 0.
400 if (fill_info.wrote_offset)
403 // The entries have been filled in from the FUSE filesystem, now write them
404 // out to the buffer.
405 error = getdents.GetDents(offs, pdir, count, out_bytes);
412 if (opened_dir && fuse_ops_->releasedir) {
413 // Ignore this result, we're already failing.
414 fuse_ops_->releasedir(path_.c_str(), &info_);
420 int DirFuseFsNode::FillDirCallback(void* buf,
422 const struct stat* stbuf,
424 FillDirInfo* fill_info = static_cast<FillDirInfo*>(buf);
426 // It is OK for the FUSE filesystem to pass a NULL stbuf. In that case, just
428 ino_t ino = stbuf ? stbuf->st_ino : 1;
430 // The FUSE docs say that the implementor of readdir can choose to ignore the
431 // offset given, and instead return all entries. To do this, they pass
432 // |off| == 0 for each call.
434 if (fill_info->num_bytes < sizeof(dirent))
435 return 1; // 1 => buffer is full
437 fill_info->wrote_offset = true;
438 fill_info->getdents->AddDirent(ino, name, strlen(name));
439 fill_info->num_bytes -= sizeof(dirent);
440 // return 0 => request more data. return 1 => buffer full.
441 return fill_info->num_bytes > 0 ? 0 : 1;
443 fill_info->getdents->AddDirent(ino, name, strlen(name));
444 fill_info->num_bytes -= sizeof(dirent);
445 // According to the docs, we can never return 1 (buffer full) when the
446 // offset is zero (the user is probably ignoring the result anyway).
451 } // namespace nacl_io