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/dir_node.h"
6
7#include <errno.h>
8#include <string.h>
9
10#include "nacl_io/log.h"
11#include "nacl_io/osdirent.h"
12#include "nacl_io/osinttypes.h"
13#include "nacl_io/osstat.h"
14#include "sdk_util/auto_lock.h"
15#include "sdk_util/macros.h"
16
17namespace nacl_io {
18
19namespace {
20
21// TODO(binji): For now, just use a dummy value for the parent ino.
22const ino_t kParentDirIno = -1;
23}
24
25DirNode::DirNode(Filesystem* filesystem)
26    : Node(filesystem),
27      cache_(stat_.st_ino, kParentDirIno),
28      cache_built_(false) {
29  SetType(S_IFDIR);
30  // Directories are raadable, writable and executable by default.
31  stat_.st_mode |= S_IRALL | S_IWALL | S_IXALL;
32}
33
34DirNode::~DirNode() {
35  for (NodeMap_t::iterator it = map_.begin(); it != map_.end(); ++it) {
36    it->second->Unlink();
37  }
38}
39
40Error DirNode::Read(const HandleAttr& attr,
41                    void* buf,
42                    size_t count,
43                    int* out_bytes) {
44  *out_bytes = 0;
45  LOG_TRACE("Can't read a directory.");
46  return EISDIR;
47}
48
49Error DirNode::FTruncate(off_t size) {
50  LOG_TRACE("Can't truncate a directory.");
51  return EISDIR;
52}
53
54Error DirNode::Write(const HandleAttr& attr,
55                     const void* buf,
56                     size_t count,
57                     int* out_bytes) {
58  *out_bytes = 0;
59  LOG_TRACE("Can't write to a directory.");
60  return EISDIR;
61}
62
63Error DirNode::GetDents(size_t offs,
64                        dirent* pdir,
65                        size_t size,
66                        int* out_bytes) {
67  AUTO_LOCK(node_lock_);
68  BuildCache_Locked();
69  return cache_.GetDents(offs, pdir, size, out_bytes);
70}
71
72Error DirNode::Fchmod(mode_t mode) {
73  AUTO_LOCK(node_lock_);
74  SetMode(mode);
75  return 0;
76}
77
78Error DirNode::AddChild(const std::string& name, const ScopedNode& node) {
79  AUTO_LOCK(node_lock_);
80
81  if (name.empty()) {
82    LOG_ERROR("Can't add child with no name.");
83    return ENOENT;
84  }
85
86  if (name.length() >= MEMBER_SIZE(dirent, d_name)) {
87    LOG_ERROR("Child name is too long: %" PRIuS " >= %" PRIuS,
88              name.length(),
89              MEMBER_SIZE(dirent, d_name));
90    return ENAMETOOLONG;
91  }
92
93  NodeMap_t::iterator it = map_.find(name);
94  if (it != map_.end()) {
95    LOG_TRACE("Can't add child \"%s\", it already exists.", name);
96    return EEXIST;
97  }
98
99  node->Link();
100  map_[name] = node;
101  ClearCache_Locked();
102  return 0;
103}
104
105Error DirNode::RemoveChild(const std::string& name) {
106  AUTO_LOCK(node_lock_);
107  NodeMap_t::iterator it = map_.find(name);
108  if (it != map_.end()) {
109    it->second->Unlink();
110    map_.erase(it);
111    ClearCache_Locked();
112    return 0;
113  }
114  return ENOENT;
115}
116
117Error DirNode::FindChild(const std::string& name, ScopedNode* out_node) {
118  out_node->reset(NULL);
119
120  AUTO_LOCK(node_lock_);
121  NodeMap_t::iterator it = map_.find(name);
122  if (it == map_.end())
123    return ENOENT;
124
125  *out_node = it->second;
126  return 0;
127}
128
129int DirNode::ChildCount() {
130  AUTO_LOCK(node_lock_);
131  return map_.size();
132}
133
134void DirNode::BuildCache_Locked() {
135  if (cache_built_)
136    return;
137
138  for (NodeMap_t::iterator it = map_.begin(), end = map_.end(); it != end;
139       ++it) {
140    const std::string& name = it->first;
141    ino_t ino = it->second->stat_.st_ino;
142    cache_.AddDirent(ino, name.c_str(), name.length());
143  }
144
145  cache_built_ = true;
146}
147
148void DirNode::ClearCache_Locked() {
149  cache_built_ = false;
150  cache_.Reset();
151}
152
153}  // namespace nacl_io
154