Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / libraries / nacl_io / fusefs / fuse_fs.cc
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.
4
5 #include "nacl_io/fusefs/fuse_fs.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <string.h>
10
11 #include <algorithm>
12
13 #include "nacl_io/getdents_helper.h"
14 #include "nacl_io/kernel_handle.h"
15 #include "nacl_io/log.h"
16 #include "sdk_util/macros.h"
17
18 namespace nacl_io {
19
20 namespace {
21
22 struct FillDirInfo {
23   FillDirInfo(GetDentsHelper* getdents, size_t num_bytes)
24       : getdents(getdents), num_bytes(num_bytes), wrote_offset(false) {}
25
26   GetDentsHelper* getdents;
27   size_t num_bytes;
28   bool wrote_offset;
29 };
30
31 }  // namespace
32
33 FuseFs::FuseFs() : fuse_ops_(NULL), fuse_user_data_(NULL) {
34 }
35
36 Error FuseFs::Init(const FsInitArgs& args) {
37   Error error = Filesystem::Init(args);
38   if (error)
39     return error;
40
41   fuse_ops_ = args.fuse_ops;
42   if (fuse_ops_ == NULL) {
43     LOG_ERROR("fuse_ops_ is NULL.");
44     return EINVAL;
45   }
46
47   if (fuse_ops_->init) {
48     struct fuse_conn_info info;
49     fuse_user_data_ = fuse_ops_->init(&info);
50   }
51
52   return 0;
53 }
54
55 void FuseFs::Destroy() {
56   if (fuse_ops_ && fuse_ops_->destroy)
57     fuse_ops_->destroy(fuse_user_data_);
58 }
59
60 Error FuseFs::Access(const Path& path, int a_mode) {
61   if (!fuse_ops_->access) {
62     LOG_TRACE("fuse_ops_->access is NULL.");
63     return ENOSYS;
64   }
65
66   int result = fuse_ops_->access(path.Join().c_str(), a_mode);
67   if (result < 0)
68     return -result;
69
70   return 0;
71 }
72
73 Error FuseFs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
74   std::string path_str = path.Join();
75   const char* path_cstr = path_str.c_str();
76   int result = 0;
77
78   struct fuse_file_info fi;
79   memset(&fi, 0, sizeof(fi));
80   fi.flags = open_flags;
81
82   if (open_flags & (O_CREAT | O_EXCL)) {
83     // According to the FUSE docs, open() is not called when O_CREAT or O_EXCL
84     // is passed.
85     mode_t mode = S_IRALL | S_IWALL;
86     if (fuse_ops_->create) {
87       result = fuse_ops_->create(path_cstr, mode, &fi);
88       if (result < 0)
89         return -result;
90     } else if (fuse_ops_->mknod) {
91       result = fuse_ops_->mknod(path_cstr, mode, dev_);
92       if (result < 0)
93         return -result;
94     } else {
95       LOG_TRACE("fuse_ops_->create and fuse_ops_->mknod are NULL.");
96       return ENOSYS;
97     }
98   } else {
99     // First determine if this is a regular file or a directory.
100     if (fuse_ops_->getattr) {
101       struct stat statbuf;
102       result = fuse_ops_->getattr(path_cstr, &statbuf);
103       if (result < 0)
104         return -result;
105
106       if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
107         // This is a directory. Don't try to open, just create a new node with
108         // this path.
109         ScopedNode node(new DirFuseFsNode(this, fuse_ops_, fi, path_cstr));
110         Error error = node->Init(open_flags);
111         if (error)
112           return error;
113
114         *out_node = node;
115         return 0;
116       }
117     }
118
119     // Existing file.
120     if (open_flags & O_TRUNC) {
121       // According to the FUSE docs, O_TRUNC does two calls: first truncate()
122       // then open().
123       if (!fuse_ops_->truncate) {
124         LOG_TRACE("fuse_ops_->truncate is NULL.");
125         return ENOSYS;
126       }
127       result = fuse_ops_->truncate(path_cstr, 0);
128       if (result < 0)
129         return -result;
130     }
131
132     if (!fuse_ops_->open) {
133       LOG_TRACE("fuse_ops_->open is NULL.");
134       return ENOSYS;
135     }
136     result = fuse_ops_->open(path_cstr, &fi);
137     if (result < 0)
138       return -result;
139   }
140
141   ScopedNode node(new FileFuseFsNode(this, fuse_ops_, fi, path_cstr));
142   Error error = node->Init(open_flags);
143   if (error)
144     return error;
145
146   *out_node = node;
147   return 0;
148 }
149
150 Error FuseFs::Unlink(const Path& path) {
151   if (!fuse_ops_->unlink) {
152     LOG_TRACE("fuse_ops_->unlink is NULL.");
153     return ENOSYS;
154   }
155
156   int result = fuse_ops_->unlink(path.Join().c_str());
157   if (result < 0)
158     return -result;
159
160   return 0;
161 }
162
163 Error FuseFs::Mkdir(const Path& path, int perm) {
164   if (!fuse_ops_->mkdir) {
165     LOG_TRACE("fuse_ops_->mkdir is NULL.");
166     return ENOSYS;
167   }
168
169   int result = fuse_ops_->mkdir(path.Join().c_str(), perm);
170   if (result < 0)
171     return -result;
172
173   return 0;
174 }
175
176 Error FuseFs::Rmdir(const Path& path) {
177   if (!fuse_ops_->rmdir) {
178     LOG_TRACE("fuse_ops_->rmdir is NULL.");
179     return ENOSYS;
180   }
181
182   int result = fuse_ops_->rmdir(path.Join().c_str());
183   if (result < 0)
184     return -result;
185
186   return 0;
187 }
188
189 Error FuseFs::Remove(const Path& path) {
190   ScopedNode node;
191   Error error = Open(path, O_RDONLY, &node);
192   if (error)
193     return error;
194
195   struct stat statbuf;
196   error = node->GetStat(&statbuf);
197   if (error)
198     return error;
199
200   node.reset();
201
202   if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
203     return Rmdir(path);
204   } else {
205     return Unlink(path);
206   }
207 }
208
209 Error FuseFs::Rename(const Path& path, const Path& newpath) {
210   if (!fuse_ops_->rename) {
211     LOG_TRACE("fuse_ops_->rename is NULL.");
212     return ENOSYS;
213   }
214
215   int result = fuse_ops_->rename(path.Join().c_str(), newpath.Join().c_str());
216   if (result < 0)
217     return -result;
218
219   return 0;
220 }
221
222 FuseFsNode::FuseFsNode(Filesystem* filesystem,
223                        struct fuse_operations* fuse_ops,
224                        struct fuse_file_info& info,
225                        const std::string& path)
226     : Node(filesystem), fuse_ops_(fuse_ops), info_(info), path_(path) {
227 }
228
229 bool FuseFsNode::CanOpen(int open_flags) {
230   struct stat statbuf;
231   Error error = GetStat(&statbuf);
232   if (error)
233     return false;
234
235   // GetStat cached the mode in stat_.st_mode. Forward to Node::CanOpen,
236   // which will check this mode against open_flags.
237   return Node::CanOpen(open_flags);
238 }
239
240 Error FuseFsNode::GetStat(struct stat* stat) {
241   int result;
242   if (fuse_ops_->fgetattr) {
243     result = fuse_ops_->fgetattr(path_.c_str(), stat, &info_);
244     if (result < 0)
245       return -result;
246   } else if (fuse_ops_->getattr) {
247     result = fuse_ops_->getattr(path_.c_str(), stat);
248     if (result < 0)
249       return -result;
250   } else {
251     LOG_TRACE("fuse_ops_->fgetattr and fuse_ops_->getattr are NULL.");
252     return ENOSYS;
253   }
254
255   // Also update the cached stat values.
256   stat_ = *stat;
257   return 0;
258 }
259
260 Error FuseFsNode::VIoctl(int request, va_list args) {
261   LOG_ERROR("Ioctl not implemented for fusefs.");
262   return ENOSYS;
263 }
264
265 Error FuseFsNode::Tcflush(int queue_selector) {
266   LOG_ERROR("Tcflush not implemented for fusefs.");
267   return ENOSYS;
268 }
269
270 Error FuseFsNode::Tcgetattr(struct termios* termios_p) {
271   LOG_ERROR("Tcgetattr not implemented for fusefs.");
272   return ENOSYS;
273 }
274
275 Error FuseFsNode::Tcsetattr(int optional_actions,
276                             const struct termios* termios_p) {
277   LOG_ERROR("Tcsetattr not implemented for fusefs.");
278   return ENOSYS;
279 }
280
281 Error FuseFsNode::GetSize(off_t* out_size) {
282   struct stat statbuf;
283   Error error = GetStat(&statbuf);
284   if (error)
285     return error;
286
287   *out_size = stat_.st_size;
288   return 0;
289 }
290
291 FileFuseFsNode::FileFuseFsNode(Filesystem* filesystem,
292                                struct fuse_operations* fuse_ops,
293                                struct fuse_file_info& info,
294                                const std::string& path)
295     : FuseFsNode(filesystem, fuse_ops, info, path) {
296 }
297
298 void FileFuseFsNode::Destroy() {
299   if (!fuse_ops_->release)
300     return;
301   fuse_ops_->release(path_.c_str(), &info_);
302 }
303
304 Error FileFuseFsNode::FSync() {
305   if (!fuse_ops_->fsync) {
306     LOG_ERROR("fuse_ops_->fsync is NULL.");
307     return ENOSYS;
308   }
309
310   int datasync = 0;
311   int result = fuse_ops_->fsync(path_.c_str(), datasync, &info_);
312   if (result < 0)
313     return -result;
314   return 0;
315 }
316
317 Error FileFuseFsNode::FTruncate(off_t length) {
318   if (!fuse_ops_->ftruncate) {
319     LOG_ERROR("fuse_ops_->ftruncate is NULL.");
320     return ENOSYS;
321   }
322
323   int result = fuse_ops_->ftruncate(path_.c_str(), length, &info_);
324   if (result < 0)
325     return -result;
326   return 0;
327 }
328
329 Error FileFuseFsNode::Read(const HandleAttr& attr,
330                            void* buf,
331                            size_t count,
332                            int* out_bytes) {
333   if (!fuse_ops_->read) {
334     LOG_ERROR("fuse_ops_->read is NULL.");
335     return ENOSYS;
336   }
337
338   char* cbuf = static_cast<char*>(buf);
339
340   int result = fuse_ops_->read(path_.c_str(), cbuf, count, attr.offs, &info_);
341   if (result < 0)
342     return -result;
343
344   // Fuse docs say that a read() call will always completely fill the buffer
345   // (padding with zeroes) unless the direct_io filesystem flag is set.
346   // TODO(binji): support the direct_io flag
347   if (static_cast<size_t>(result) < count)
348     memset(&cbuf[result], 0, count - result);
349
350   *out_bytes = count;
351   return 0;
352 }
353
354 Error FileFuseFsNode::Write(const HandleAttr& attr,
355                             const void* buf,
356                             size_t count,
357                             int* out_bytes) {
358   if (!fuse_ops_->write) {
359     LOG_ERROR("fuse_ops_->write is NULL.");
360     return ENOSYS;
361   }
362
363   int result = fuse_ops_->write(
364       path_.c_str(), static_cast<const char*>(buf), count, attr.offs, &info_);
365   if (result < 0)
366     return -result;
367
368   // Fuse docs say that a write() call will always write the entire buffer
369   // unless the direct_io filesystem flag is set.
370   // TODO(binji): What should we do if the user breaks this contract? Warn?
371   // TODO(binji): support the direct_io flag
372   *out_bytes = result;
373   return 0;
374 }
375
376 DirFuseFsNode::DirFuseFsNode(Filesystem* filesystem,
377                              struct fuse_operations* fuse_ops,
378                              struct fuse_file_info& info,
379                              const std::string& path)
380     : FuseFsNode(filesystem, fuse_ops, info, path) {
381 }
382
383 void DirFuseFsNode::Destroy() {
384   if (!fuse_ops_->releasedir)
385     return;
386   fuse_ops_->releasedir(path_.c_str(), &info_);
387 }
388
389 Error DirFuseFsNode::FSync() {
390   if (!fuse_ops_->fsyncdir) {
391     LOG_ERROR("fuse_ops_->fsyncdir is NULL.");
392     return ENOSYS;
393   }
394
395   int datasync = 0;
396   int result = fuse_ops_->fsyncdir(path_.c_str(), datasync, &info_);
397   if (result < 0)
398     return -result;
399   return 0;
400 }
401
402 Error DirFuseFsNode::GetDents(size_t offs,
403                               struct dirent* pdir,
404                               size_t count,
405                               int* out_bytes) {
406   if (!fuse_ops_->readdir) {
407     LOG_ERROR("fuse_ops_->readdir is NULL.");
408     return ENOSYS;
409   }
410
411   bool opened_dir = false;
412   int result;
413
414   // Opendir is not necessary, only readdir. Call it anyway, if it is defined.
415   if (fuse_ops_->opendir) {
416     result = fuse_ops_->opendir(path_.c_str(), &info_);
417     if (result < 0)
418       return -result;
419
420     opened_dir = true;
421   }
422
423   Error error = 0;
424   GetDentsHelper getdents;
425   FillDirInfo fill_info(&getdents, count);
426   result = fuse_ops_->readdir(
427       path_.c_str(), &fill_info, &DirFuseFsNode::FillDirCallback, offs, &info_);
428   if (result < 0)
429     goto fail;
430
431   // If the fill function ever wrote an entry with |offs| != 0, then assume it
432   // was not given the full list of entries. In that case, GetDentsHelper's
433   // buffers start with the entry at offset |offs|, so the call to
434   // GetDentsHelpers::GetDents should use an offset of 0.
435   if (fill_info.wrote_offset)
436     offs = 0;
437
438   // The entries have been filled in from the FUSE filesystem, now write them
439   // out to the buffer.
440   error = getdents.GetDents(offs, pdir, count, out_bytes);
441   if (error)
442     goto fail;
443
444   return 0;
445
446 fail:
447   if (opened_dir && fuse_ops_->releasedir) {
448     // Ignore this result, we're already failing.
449     fuse_ops_->releasedir(path_.c_str(), &info_);
450   }
451
452   return -result;
453 }
454
455 int DirFuseFsNode::FillDirCallback(void* buf,
456                                    const char* name,
457                                    const struct stat* stbuf,
458                                    off_t off) {
459   FillDirInfo* fill_info = static_cast<FillDirInfo*>(buf);
460
461   // It is OK for the FUSE filesystem to pass a NULL stbuf. In that case, just
462   // use a bogus ino.
463   ino_t ino = stbuf ? stbuf->st_ino : 1;
464
465   // The FUSE docs say that the implementor of readdir can choose to ignore the
466   // offset given, and instead return all entries. To do this, they pass
467   // |off| == 0 for each call.
468   if (off) {
469     if (fill_info->num_bytes < sizeof(dirent))
470       return 1;  // 1 => buffer is full
471
472     fill_info->wrote_offset = true;
473     fill_info->getdents->AddDirent(ino, name, strlen(name));
474     fill_info->num_bytes -= sizeof(dirent);
475     // return 0 => request more data. return 1 => buffer full.
476     return fill_info->num_bytes > 0 ? 0 : 1;
477   } else {
478     fill_info->getdents->AddDirent(ino, name, strlen(name));
479     fill_info->num_bytes -= sizeof(dirent);
480     // According to the docs, we can never return 1 (buffer full) when the
481     // offset is zero (the user is probably ignoring the result anyway).
482     return 0;
483   }
484 }
485
486 }  // namespace nacl_io