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