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