Upstream version 7.36.149.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 "sdk_util/macros.h"
16
17 namespace nacl_io {
18
19 namespace {
20
21 struct FillDirInfo {
22   FillDirInfo(GetDentsHelper* getdents, size_t num_bytes)
23       : getdents(getdents), num_bytes(num_bytes), wrote_offset(false) {}
24
25   GetDentsHelper* getdents;
26   size_t num_bytes;
27   bool wrote_offset;
28 };
29
30 }  // namespace
31
32 FuseFs::FuseFs() : fuse_ops_(NULL), fuse_user_data_(NULL) {}
33
34 Error FuseFs::Init(const FsInitArgs& args) {
35   Error error = Filesystem::Init(args);
36   if (error)
37     return error;
38
39   fuse_ops_ = args.fuse_ops;
40   if (fuse_ops_ == NULL)
41     return EINVAL;
42
43   if (fuse_ops_->init) {
44     struct fuse_conn_info info;
45     fuse_user_data_ = fuse_ops_->init(&info);
46   }
47
48   return 0;
49 }
50
51 void FuseFs::Destroy() {
52   if (fuse_ops_ && fuse_ops_->destroy)
53     fuse_ops_->destroy(fuse_user_data_);
54 }
55
56 Error FuseFs::Access(const Path& path, int a_mode) {
57   if (!fuse_ops_->access)
58     return ENOSYS;
59
60   int result = fuse_ops_->access(path.Join().c_str(), a_mode);
61   if (result < 0)
62     return -result;
63
64   return 0;
65 }
66
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();
70   int result = 0;
71
72   struct fuse_file_info fi;
73   memset(&fi, 0, sizeof(fi));
74   fi.flags = open_flags;
75
76   if (open_flags & (O_CREAT | O_EXCL)) {
77     // According to the FUSE docs, open() is not called when O_CREAT or O_EXCL
78     // is passed.
79     mode_t mode = S_IRALL | S_IWALL;
80     if (fuse_ops_->create) {
81       result = fuse_ops_->create(path_cstr, mode, &fi);
82       if (result < 0)
83         return -result;
84     } else if (fuse_ops_->mknod) {
85       result = fuse_ops_->mknod(path_cstr, mode, dev_);
86       if (result < 0)
87         return -result;
88     } else {
89       return ENOSYS;
90     }
91   } else {
92     // First determine if this is a regular file or a directory.
93     if (fuse_ops_->getattr) {
94       struct stat statbuf;
95       result = fuse_ops_->getattr(path_cstr, &statbuf);
96       if (result < 0)
97         return -result;
98
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
101         // this path.
102         ScopedNode node(new DirFuseFsNode(this, fuse_ops_, fi, path_cstr));
103         Error error = node->Init(open_flags);
104         if (error)
105           return error;
106
107         *out_node = node;
108         return 0;
109       }
110     }
111
112     // Existing file.
113     if (open_flags & O_TRUNC) {
114       // According to the FUSE docs, O_TRUNC does two calls: first truncate()
115       // then open().
116       if (!fuse_ops_->truncate)
117         return ENOSYS;
118       result = fuse_ops_->truncate(path_cstr, 0);
119       if (result < 0)
120         return -result;
121     }
122
123     if (!fuse_ops_->open)
124       return ENOSYS;
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
135   *out_node = node;
136   return 0;
137 }
138
139 Error FuseFs::Unlink(const Path& path) {
140   if (!fuse_ops_->unlink)
141     return ENOSYS;
142
143   int result = fuse_ops_->unlink(path.Join().c_str());
144   if (result < 0)
145     return -result;
146
147   return 0;
148 }
149
150 Error FuseFs::Mkdir(const Path& path, int perm) {
151   if (!fuse_ops_->mkdir)
152     return ENOSYS;
153
154   int result = fuse_ops_->mkdir(path.Join().c_str(), perm);
155   if (result < 0)
156     return -result;
157
158   return 0;
159 }
160
161 Error FuseFs::Rmdir(const Path& path) {
162   if (!fuse_ops_->rmdir)
163     return ENOSYS;
164
165   int result = fuse_ops_->rmdir(path.Join().c_str());
166   if (result < 0)
167     return -result;
168
169   return 0;
170 }
171
172 Error FuseFs::Remove(const Path& path) {
173   ScopedNode node;
174   Error error = Open(path, O_RDONLY, &node);
175   if (error)
176     return error;
177
178   struct stat statbuf;
179   error = node->GetStat(&statbuf);
180   if (error)
181     return error;
182
183   node.reset();
184
185   if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
186     return Rmdir(path);
187   } else {
188     return Unlink(path);
189   }
190 }
191
192 Error FuseFs::Rename(const Path& path, const Path& newpath) {
193   if (!fuse_ops_->rename)
194     return ENOSYS;
195
196   int result = fuse_ops_->rename(path.Join().c_str(), newpath.Join().c_str());
197   if (result < 0)
198     return -result;
199
200   return 0;
201 }
202
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) {}
208
209 bool FuseFsNode::CanOpen(int open_flags) {
210   struct stat statbuf;
211   Error error = GetStat(&statbuf);
212   if (error)
213     return false;
214
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);
218 }
219
220 Error FuseFsNode::GetStat(struct stat* stat) {
221   int result;
222   if (fuse_ops_->fgetattr) {
223     result = fuse_ops_->fgetattr(path_.c_str(), stat, &info_);
224     if (result < 0)
225       return -result;
226   } else if (fuse_ops_->getattr) {
227     result = fuse_ops_->getattr(path_.c_str(), stat);
228     if (result < 0)
229       return -result;
230   } else {
231     return ENOSYS;
232   }
233
234   // Also update the cached stat values.
235   stat_ = *stat;
236   return 0;
237 }
238
239 Error FuseFsNode::VIoctl(int request, va_list args) {
240   // TODO(binji): implement
241   return ENOSYS;
242 }
243
244 Error FuseFsNode::Tcflush(int queue_selector) {
245   // TODO(binji): use ioctl for this?
246   return ENOSYS;
247 }
248
249 Error FuseFsNode::Tcgetattr(struct termios* termios_p) {
250   // TODO(binji): use ioctl for this?
251   return ENOSYS;
252 }
253
254 Error FuseFsNode::Tcsetattr(int optional_actions,
255                             const struct termios* termios_p) {
256   // TODO(binji): use ioctl for this?
257   return ENOSYS;
258 }
259
260 Error FuseFsNode::GetSize(off_t* out_size) {
261   struct stat statbuf;
262   Error error = GetStat(&statbuf);
263   if (error)
264     return error;
265
266   *out_size = stat_.st_size;
267   return 0;
268 }
269
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) {}
275
276 void FileFuseFsNode::Destroy() {
277   if (!fuse_ops_->release)
278     return;
279   fuse_ops_->release(path_.c_str(), &info_);
280 }
281
282 Error FileFuseFsNode::FSync() {
283   if (!fuse_ops_->fsync)
284     return ENOSYS;
285
286   int datasync = 0;
287   int result = fuse_ops_->fsync(path_.c_str(), datasync, &info_);
288   if (result < 0)
289     return -result;
290   return 0;
291 }
292
293 Error FileFuseFsNode::FTruncate(off_t length) {
294   if (!fuse_ops_->ftruncate)
295     return ENOSYS;
296
297   int result = fuse_ops_->ftruncate(path_.c_str(), length, &info_);
298   if (result < 0)
299     return -result;
300   return 0;
301 }
302
303 Error FileFuseFsNode::Read(const HandleAttr& attr,
304                            void* buf,
305                            size_t count,
306                            int* out_bytes) {
307   if (!fuse_ops_->read)
308     return ENOSYS;
309
310   char* cbuf = static_cast<char*>(buf);
311
312   int result = fuse_ops_->read(path_.c_str(), cbuf, count, attr.offs, &info_);
313   if (result < 0)
314     return -result;
315
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);
321
322   *out_bytes = count;
323   return 0;
324 }
325
326 Error FileFuseFsNode::Write(const HandleAttr& attr,
327                             const void* buf,
328                             size_t count,
329                             int* out_bytes) {
330   if (!fuse_ops_->write)
331     return ENOSYS;
332
333   int result = fuse_ops_->write(
334       path_.c_str(), static_cast<const char*>(buf), count, attr.offs, &info_);
335   if (result < 0)
336     return -result;
337
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
342   *out_bytes = result;
343   return 0;
344 }
345
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) {}
351
352 void DirFuseFsNode::Destroy() {
353   if (!fuse_ops_->releasedir)
354     return;
355   fuse_ops_->releasedir(path_.c_str(), &info_);
356 }
357
358 Error DirFuseFsNode::FSync() {
359   if (!fuse_ops_->fsyncdir)
360     return ENOSYS;
361
362   int datasync = 0;
363   int result = fuse_ops_->fsyncdir(path_.c_str(), datasync, &info_);
364   if (result < 0)
365     return -result;
366   return 0;
367 }
368
369 Error DirFuseFsNode::GetDents(size_t offs,
370                               struct dirent* pdir,
371                               size_t count,
372                               int* out_bytes) {
373   if (!fuse_ops_->readdir)
374     return ENOSYS;
375
376   bool opened_dir = false;
377   int result;
378
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_);
382     if (result < 0)
383       return -result;
384
385     opened_dir = true;
386   }
387
388   Error error = 0;
389   GetDentsHelper getdents;
390   FillDirInfo fill_info(&getdents, count);
391   result = fuse_ops_->readdir(
392       path_.c_str(), &fill_info, &DirFuseFsNode::FillDirCallback, offs, &info_);
393   if (result < 0)
394     goto fail;
395
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)
401     offs = 0;
402
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);
406   if (error)
407     goto fail;
408
409   return 0;
410
411 fail:
412   if (opened_dir && fuse_ops_->releasedir) {
413     // Ignore this result, we're already failing.
414     fuse_ops_->releasedir(path_.c_str(), &info_);
415   }
416
417   return -result;
418 }
419
420 int DirFuseFsNode::FillDirCallback(void* buf,
421                                    const char* name,
422                                    const struct stat* stbuf,
423                                    off_t off) {
424   FillDirInfo* fill_info = static_cast<FillDirInfo*>(buf);
425
426   // It is OK for the FUSE filesystem to pass a NULL stbuf. In that case, just
427   // use a bogus ino.
428   ino_t ino = stbuf ? stbuf->st_ino : 1;
429
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.
433   if (off) {
434     if (fill_info->num_bytes < sizeof(dirent))
435       return 1;  // 1 => buffer is full
436
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;
442   } else {
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).
447     return 0;
448   }
449 }
450
451 }  // namespace nacl_io