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
17 namespace nacl_io {
18
19 namespace {
20
21 // TODO(binji): For now, just use a dummy value for the parent ino.
22 const ino_t kParentDirIno = -1;
23 }
24
DirNode(Filesystem * filesystem,mode_t mode)25 DirNode::DirNode(Filesystem* filesystem, mode_t mode)
26 : Node(filesystem),
27 cache_(stat_.st_ino, kParentDirIno),
28 cache_built_(false) {
29 SetType(S_IFDIR);
30 SetMode(mode);
31 UpdateTime(UPDATE_ATIME | UPDATE_MTIME | UPDATE_CTIME);
32 }
33
~DirNode()34 DirNode::~DirNode() {
35 for (NodeMap_t::iterator it = map_.begin(); it != map_.end(); ++it) {
36 it->second->Unlink();
37 }
38 }
39
Read(const HandleAttr & attr,void * buf,size_t count,int * out_bytes)40 Error 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
FTruncate(off_t size)49 Error DirNode::FTruncate(off_t size) {
50 LOG_TRACE("Can't truncate a directory.");
51 return EISDIR;
52 }
53
Write(const HandleAttr & attr,const void * buf,size_t count,int * out_bytes)54 Error 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
GetDents(size_t offs,dirent * pdir,size_t size,int * out_bytes)63 Error 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 UpdateTime(UPDATE_ATIME);
70 return cache_.GetDents(offs, pdir, size, out_bytes);
71 }
72
Fchmod(mode_t mode)73 Error DirNode::Fchmod(mode_t mode) {
74 AUTO_LOCK(node_lock_);
75 SetMode(mode);
76 UpdateTime(UPDATE_CTIME);
77 return 0;
78 }
79
AddChild(const std::string & name,const ScopedNode & node)80 Error DirNode::AddChild(const std::string& name, const ScopedNode& node) {
81 AUTO_LOCK(node_lock_);
82
83 if (name.empty()) {
84 LOG_ERROR("Can't add child with no name.");
85 return ENOENT;
86 }
87
88 if (name.length() >= MEMBER_SIZE(dirent, d_name)) {
89 LOG_ERROR("Child name is too long: %" PRIuS " >= %" PRIuS,
90 name.length(),
91 MEMBER_SIZE(dirent, d_name));
92 return ENAMETOOLONG;
93 }
94
95 NodeMap_t::iterator it = map_.find(name);
96 if (it != map_.end()) {
97 LOG_TRACE("Can't add child \"%s\", it already exists.", name.c_str());
98 return EEXIST;
99 }
100
101 UpdateTime(UPDATE_MTIME | UPDATE_CTIME);
102
103 node->Link();
104 map_[name] = node;
105 ClearCache_Locked();
106 return 0;
107 }
108
RemoveChild(const std::string & name)109 Error DirNode::RemoveChild(const std::string& name) {
110 AUTO_LOCK(node_lock_);
111 NodeMap_t::iterator it = map_.find(name);
112 if (it != map_.end()) {
113 UpdateTime(UPDATE_MTIME | UPDATE_CTIME);
114 it->second->Unlink();
115 map_.erase(it);
116 ClearCache_Locked();
117 return 0;
118 }
119 return ENOENT;
120 }
121
FindChild(const std::string & name,ScopedNode * out_node)122 Error DirNode::FindChild(const std::string& name, ScopedNode* out_node) {
123 out_node->reset(NULL);
124
125 AUTO_LOCK(node_lock_);
126 NodeMap_t::iterator it = map_.find(name);
127 if (it == map_.end())
128 return ENOENT;
129
130 *out_node = it->second;
131 return 0;
132 }
133
ChildCount()134 int DirNode::ChildCount() {
135 AUTO_LOCK(node_lock_);
136 return map_.size();
137 }
138
BuildCache_Locked()139 void DirNode::BuildCache_Locked() {
140 if (cache_built_)
141 return;
142
143 for (NodeMap_t::iterator it = map_.begin(), end = map_.end(); it != end;
144 ++it) {
145 const std::string& name = it->first;
146 ino_t ino = it->second->stat_.st_ino;
147 cache_.AddDirent(ino, name.c_str(), name.length());
148 }
149
150 cache_built_ = true;
151 }
152
ClearCache_Locked()153 void DirNode::ClearCache_Locked() {
154 cache_built_ = false;
155 cache_.Reset();
156 }
157
158 } // namespace nacl_io
159