1// Copyright (c) 2012 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/mount_node_dir.h"
6
7#include <errno.h>
8#include <string.h>
9
10#include "nacl_io/osdirent.h"
11#include "nacl_io/osstat.h"
12#include "sdk_util/auto_lock.h"
13#include "sdk_util/macros.h"
14
15namespace nacl_io {
16
17MountNodeDir::MountNodeDir(Mount* mount) : MountNode(mount), cache_(NULL) {
18  stat_.st_mode |= S_IFDIR;
19}
20
21MountNodeDir::~MountNodeDir() {
22  for (MountNodeMap_t::iterator it = map_.begin(); it != map_.end(); ++it) {
23    it->second->Unlink();
24  }
25  free(cache_);
26}
27
28Error MountNodeDir::Read(size_t offs, void* buf, size_t count, int* out_bytes) {
29  *out_bytes = 0;
30  return EISDIR;
31}
32
33Error MountNodeDir::FTruncate(off_t size) { return EISDIR; }
34
35Error MountNodeDir::Write(size_t offs,
36                          const void* buf,
37                          size_t count,
38                          int* out_bytes) {
39  *out_bytes = 0;
40  return EISDIR;
41}
42
43Error MountNodeDir::GetDents(size_t offs,
44                             struct dirent* pdir,
45                             size_t size,
46                             int* out_bytes) {
47  *out_bytes = 0;
48
49  AUTO_LOCK(node_lock_);
50
51  // If the buffer pointer is invalid, fail
52  if (NULL == pdir)
53    return EINVAL;
54
55  // If the buffer is too small, fail
56  if (size < sizeof(struct dirent))
57    return EINVAL;
58
59  // Force size to a multiple of dirent
60  size -= size % sizeof(struct dirent);
61  size_t max = map_.size() * sizeof(struct dirent);
62  if (cache_ == NULL)
63    BuildCache();
64
65  if (offs >= max) {
66    // OK, trying to read past the end.
67    return 0;
68  }
69
70  if (offs + size >= max)
71    size = max - offs;
72
73  memcpy(pdir, ((char*)cache_) + offs, size);
74  *out_bytes = size;
75  return 0;
76}
77
78Error MountNodeDir::AddChild(const std::string& name,
79                             const ScopedMountNode& node) {
80  AUTO_LOCK(node_lock_);
81
82  if (name.empty())
83    return ENOENT;
84
85  if (name.length() >= MEMBER_SIZE(struct dirent, d_name))
86    return ENAMETOOLONG;
87
88  MountNodeMap_t::iterator it = map_.find(name);
89  if (it != map_.end())
90    return EEXIST;
91
92  node->Link();
93  map_[name] = node;
94  ClearCache();
95  return 0;
96}
97
98Error MountNodeDir::RemoveChild(const std::string& name) {
99  AUTO_LOCK(node_lock_);
100  MountNodeMap_t::iterator it = map_.find(name);
101  if (it != map_.end()) {
102    it->second->Unlink();
103    map_.erase(it);
104    ClearCache();
105    return 0;
106  }
107  return ENOENT;
108}
109
110Error MountNodeDir::FindChild(const std::string& name,
111                              ScopedMountNode* out_node) {
112  out_node->reset(NULL);
113
114  AUTO_LOCK(node_lock_);
115  MountNodeMap_t::iterator it = map_.find(name);
116  if (it == map_.end())
117    return ENOENT;
118
119  *out_node = it->second;
120  return 0;
121}
122
123int MountNodeDir::ChildCount() {
124  AUTO_LOCK(node_lock_);
125  return map_.size();
126}
127
128void MountNodeDir::ClearCache() {
129  free(cache_);
130  cache_ = NULL;
131}
132
133void MountNodeDir::BuildCache() {
134  if (map_.size()) {
135    cache_ = (struct dirent*)malloc(sizeof(struct dirent) * map_.size());
136    MountNodeMap_t::iterator it = map_.begin();
137    for (size_t index = 0; it != map_.end(); it++, index++) {
138      size_t len = it->first.length();
139      cache_[index].d_ino = it->second->stat_.st_ino;
140      cache_[index].d_off = sizeof(struct dirent);
141      cache_[index].d_reclen = sizeof(struct dirent);
142      cache_[index].d_name[len] = 0;
143      strncpy(cache_[index].d_name, &it->first[0], len);
144    }
145  }
146}
147
148}  // namespace nacl_io
149
150