0.9.8.10
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
NameIdMapper.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007-2015 Hypertable, Inc.
3  *
4  * This file is part of Hypertable.
5  *
6  * Hypertable is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; version 3 of the
9  * License, or any later version.
10  *
11  * Hypertable is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301, USA.
20  */
21 
22 #include <Common/Compat.h>
23 
24 #include "NameIdMapper.h"
25 
26 #include <Common/Error.h>
27 #include <Common/Logger.h>
28 #include <Common/ScopeGuard.h>
29 #include <Common/StringExt.h>
30 
31 #include <boost/algorithm/string.hpp>
32 #include <boost/tokenizer.hpp>
33 
34 namespace Hypertable {
35 
36 using namespace std;
37 using namespace Hyperspace;
38 
39 
40 NameIdMapper::NameIdMapper(Hyperspace::SessionPtr &hyperspace, const string &toplevel_dir)
41  : m_hyperspace(hyperspace), m_toplevel_dir(toplevel_dir) {
42 
43  /*
44  * Prefix looks like this: "/" <toplevel_dir> "namemap" "names"
45  * This for loop adds the number of components in <toplevel_dir>
46  */
48  for (const char *ptr=toplevel_dir.c_str(); *ptr; ptr++)
49  if (*ptr == '/')
51 
52  m_names_dir = toplevel_dir + "/namemap/names";
53  m_ids_dir = toplevel_dir + "/namemap/ids";
54 
55  // Create "names" directory
56  m_hyperspace->mkdirs(m_names_dir);
57 
58  // Create "ids" directory
59  std::vector<Attribute> init_attr;
60  init_attr.push_back(Attribute("nid", "0", 1));
61  m_hyperspace->mkdirs(m_ids_dir, init_attr);
62 }
63 
64 bool NameIdMapper::name_to_id(const string &name, string &id, bool *is_namespacep) {
65  lock_guard<mutex> lock(m_mutex);
66  return do_mapping(name, false, id, is_namespacep);
67 }
68 
69 bool NameIdMapper::id_to_name(const string &id, string &name, bool *is_namespacep) {
70  lock_guard<mutex> lock(m_mutex);
71  return do_mapping(id, true, name, is_namespacep);
72 }
73 
74 void NameIdMapper::add_entry(const string &names_parent, const string &names_entry,
75  std::vector<uint64_t> &ids, bool is_namespace) {
76  uint64_t id = 0;
77  string names_file = m_names_dir + names_parent + "/" + names_entry;
78 
79  string parent_ids_file = m_ids_dir;
80  for (size_t i=0; i<ids.size(); i++)
81  parent_ids_file += String("/") + ids[i];
82 
83  if (!m_hyperspace->exists(names_file))
84  id = m_hyperspace->attr_incr(parent_ids_file, "nid");
85  else {
86  bool attr_exists;
87  DynamicBuffer dbuf;
88  m_hyperspace->attr_get(names_file, "id", attr_exists, dbuf);
89  if (attr_exists)
90  id = strtoll((const char *)dbuf.base, 0, 0);
91  else {
92  id = m_hyperspace->attr_incr(parent_ids_file, "nid");
93  UInt64Formatter buf(id);
94  m_hyperspace->attr_set(names_file, "id", buf.c_str(), buf.size());
95  }
96  ids.push_back(id);
97  return;
98  }
99 
100  // At this point "id" is properly set
101  string ids_file = parent_ids_file + String("/") + id;
102  std::vector<Attribute> attrs;
103  attrs.push_back(Attribute("name", names_entry.c_str(), names_entry.length()));
104  attrs.push_back(Attribute("nid", "0", 1));
105 
106  if (m_hyperspace->exists(ids_file)) {
107  if (is_namespace) {
108  if (!m_hyperspace->attr_exists(ids_file, "nid")) {
109  m_hyperspace->attr_set(ids_file, 0, attrs);
110  }
111  }
112  }
113  else {
114  if (is_namespace) {
115  try {
116  m_hyperspace->mkdir(ids_file, attrs);
117  }
118  catch (Exception &e) {
119  HT_ERROR_OUT << e << HT_END;
120  throw;
121  }
122  }
123  else
125  "name", names_entry.c_str(), names_entry.length());
126  }
127 
128  // At this point the ID file exists, we now need to
129  // create the names file/dir and update the "id" attribute
130 
131  UInt64Formatter buf(id);
132 
133  if (is_namespace) {
134  std::vector<Attribute> init_attr;
135  init_attr.push_back(Attribute("id", buf.c_str(), buf.size()));
136  m_hyperspace->mkdir(names_file, init_attr);
137  }
138  else {
139  // Set the "id" attribute of the names file
141  "id", buf.c_str(), buf.size());
142  }
143  ids.push_back(id);
144 }
145 
146 void NameIdMapper::add_mapping(const string &name, string &id, int flags, bool ignore_exists) {
147  lock_guard<mutex> lock(m_mutex);
148  typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
149  boost::char_separator<char> sep("/");
150  std::vector<String> name_components;
151  std::vector<uint64_t> id_components;
152  std::vector<DirEntryAttr> listing;
153  DynamicBuffer value_buf;
154 
155  tokenizer tokens(name, sep);
156  for (tokenizer::iterator tok_iter = tokens.begin();
157  tok_iter != tokens.end(); ++tok_iter)
158  name_components.push_back(*tok_iter);
159 
160  HT_ASSERT(!name_components.empty());
161 
162  string names_parent = "";
163  string names_child = "";
164 
165  listing.reserve(name_components.size());
166  for (size_t i=0; i<name_components.size()-1; i++) {
167 
168  names_child += String("/") + name_components[i];
169 
170  try {
171  string names_file = m_names_dir + names_child;
172  m_hyperspace->attr_get(names_file, "id", value_buf);
173  if (flags & CREATE_INTERMEDIATE) {
174  m_hyperspace->readpath_attr(names_file, "id", listing);
175  if (!listing.back().is_dir)
176  HT_THROW(Error::NAME_ALREADY_IN_USE, names_child);
177  }
178  id_components.push_back( strtoll((const char *)value_buf.base, 0, 0) );
179  }
180  catch (Exception &e) {
181 
184  throw;
185 
186  if (!(flags & CREATE_INTERMEDIATE))
188 
189  add_entry(names_parent, name_components[i], id_components, true);
190  }
191 
192  names_parent = names_child;
193  }
194 
195  try {
196  add_entry(names_parent, name_components.back(),
197  id_components, (bool)(flags & IS_NAMESPACE));
198  }
199  catch (Exception &e) {
200  if (!ignore_exists && e.code() == Error::HYPERSPACE_FILE_EXISTS)
201  HT_THROW(Error::NAMESPACE_EXISTS, (String)" namespace=" + name);
202  throw;
203  }
204 
205  id = (name[0] == '/') ? "/" : "";
206  id = id + id_components[0];
207 
208  for (size_t i=1; i<id_components.size(); i++)
209  id += String("/") + id_components[i];
210 }
211 
212 void NameIdMapper::rename(const string &curr_name, const string &next_name) {
213  lock_guard<mutex> lock(m_mutex);
214  string id;
216  string old_name = curr_name;
217  string new_name = next_name;
218  string new_name_last_comp;
219  size_t new_name_last_slash_pos;
220  string id_last_component;
221  size_t id_last_component_pos;
222 
223  boost::trim_if(old_name, boost::is_any_of("/ "));
224  boost::trim_if(new_name, boost::is_any_of("/ "));
225 
226  new_name_last_slash_pos = new_name.find_last_of('/');
227  if (new_name_last_slash_pos != String::npos)
228  new_name_last_comp = new_name.substr(new_name_last_slash_pos+1);
229  else
230  new_name_last_comp = new_name;
231 
232  if (do_mapping(old_name, false, id, 0)) {
233  // Set the name attribute of the id file to be the last path component of new_name
234  string id_file = m_ids_dir + "/" + id;
235  m_hyperspace->attr_set(id_file, oflags, "name", new_name_last_comp.c_str(),
236  new_name_last_comp.length());
237 
238  // Create the name file and set its id attribute
239  id_last_component_pos = id.find_last_of('/');
240  if (id_last_component_pos != String::npos)
241  id_last_component = id.substr(id_last_component_pos+1);
242  else
243  id_last_component = id;
244 
245  m_hyperspace->attr_set(m_names_dir + "/" + new_name, oflags|OPEN_FLAG_CREATE, "id", id_last_component.c_str(),
246  id_last_component.length());
247 
248  // Delete the existing name file
249  m_hyperspace->unlink(m_names_dir + "/" + old_name);
250  }
251 
252 }
253 
254 void NameIdMapper::drop_mapping(const string &name) {
255  lock_guard<mutex> lock(m_mutex);
256  string id;
257  string table_name = name;
258 
259  boost::trim_if(table_name, boost::is_any_of("/ "));
260 
261  if (do_mapping(name, false, id, 0)) {
262  try {
263  m_hyperspace->unlink(m_ids_dir + "/" + id);
264  }
265  catch (Exception &e) {
268  throw;
269  }
270  }
271 
272  try {
273  m_hyperspace->unlink(m_names_dir + "/" + table_name);
274  }
275  catch (Exception &e) {
278  throw;
279  }
280 }
281 
282 bool NameIdMapper::exists_mapping(const string &name, bool *is_namespace) {
283  string id;
284  string namespace_name = name;
285  boost::trim_if(namespace_name, boost::is_any_of("/ "));
286  return do_mapping(namespace_name, false, id, is_namespace);
287 }
288 
289 bool NameIdMapper::do_mapping(const string &input, bool id_in, string &output,
290  bool *is_namespacep) {
291  vector <DirEntryAttr> listing;
292  int num_path_components = 0;
293  string hyperspace_file;
294  string attr;
295  output = (String)"";
296 
297  if (id_in) {
298  hyperspace_file = m_ids_dir;
299  attr = (String)"name";
300  }
301  else {
302  hyperspace_file = m_names_dir;
303  attr = (String)"id";
304  }
305 
306  // deal with leading '/' in input
307  if (input.find('/') != 0)
308  hyperspace_file += "/";
309 
310  hyperspace_file += input;
311 
312  // count number of components in path (ignore trailing '/')
313  num_path_components = 1;
314  for(size_t ii=0; ii< hyperspace_file.size()-1; ++ii) {
315  if (hyperspace_file[ii] == '/')
316  ++num_path_components;
317  }
318 
319  try {
320  m_hyperspace->readpath_attr(hyperspace_file, attr, listing);
321  }
322  catch (Exception &e) {
325  HT_DEBUG_OUT << "Can't map " << input << HT_END;
326  return false;
327  }
328  throw;
329  }
330 
331  if (listing.size() != (size_t) num_path_components || listing.size() == 0)
332  return false;
333 
334  struct LtDirEntryAttr ascending;
335  sort(listing.begin(), listing.end(), ascending);
336 
337  for (size_t ii=m_prefix_components; ii<listing.size(); ++ii) {
338  string attr_val((const char*)listing[ii].attr.base);
339  output += attr_val + "/";
340  }
341 
342  // strip trailing slash
343  if (!output.empty())
344  output.resize(output.size()-1);
345 
346  bool is_dir = listing[listing.size()-1].is_dir;
347 
348  if (is_namespacep)
349  *is_namespacep = is_dir;
350 
351  return true;
352 
353 }
354 
355 void NameIdMapper::id_to_sublisting(const string &id, bool include_sub_entries, vector<NamespaceListing> &listing) {
356  vector <DirEntryAttr> dir_listing;
357  string hyperspace_dir;
358  string attr;
359 
360  hyperspace_dir = m_ids_dir;
361  attr = (String)"name";
362 
363  hyperspace_dir += (String)"/" + id;
364 
365  m_hyperspace->readdir_attr(hyperspace_dir, attr, include_sub_entries, dir_listing);
366 
367  get_namespace_listing(dir_listing, listing);
368 }
369 
370 void NameIdMapper::get_namespace_listing(const std::vector<DirEntryAttr> &dir_listing, std::vector<NamespaceListing> &listing) {
371  NamespaceListing entry;
372  listing.clear();
373  listing.reserve(dir_listing.size());
374  for (const auto &dir_entry : dir_listing) {
375  if (dir_entry.has_attr) {
376  entry.name = (String)((const char*)dir_entry.attr.base);
377  entry.is_namespace = dir_entry.is_dir;
378  entry.id = dir_entry.name;
379  listing.push_back(entry);
380  if (!dir_entry.sub_entries.empty())
381  get_namespace_listing(dir_entry.sub_entries, listing.back().sub_entries);
382  }
383  }
384 
385  struct LtNamespaceListingName ascending;
386  sort(listing.begin(), listing.end(), ascending);
387 }
388 } // namespace Hypertable
389 
bool id_to_name(const std::string &id, std::string &name, bool *is_namespacep=0)
Definition: NameIdMapper.cc:69
std::string String
A String is simply a typedef to std::string.
Definition: String.h:44
Error if create and file exists.
Definition: Session.h:79
void add_entry(const std::string &names_parent, const std::string &names_entry, std::vector< uint64_t > &ids, bool is_namespace)
Definition: NameIdMapper.cc:74
STL namespace.
void id_to_sublisting(const std::string &id, bool include_sub_entries, std::vector< NamespaceListing > &listing)
bool do_mapping(const std::string &input, bool id_in, std::string &output, bool *is_namespacep)
void drop_mapping(const std::string &name)
Drops a mapping.
A dynamic, resizable and reference counted memory buffer.
Definition: DynamicBuffer.h:42
Hyperspace definitions
#define HT_ASSERT(_e_)
Definition: Logger.h:396
Open file for writing.
Definition: Session.h:73
std::shared_ptr< Session > SessionPtr
Definition: Session.h:734
bool exists_mapping(const std::string &name, bool *is_namespace)
void add_mapping(const std::string &name, std::string &id, int flags=0, bool ignore_exists=false)
Adds a new mapping.
Logging routines and macros.
Compatibility Macros for C/C++.
Hyperspace::SessionPtr m_hyperspace
Definition: NameIdMapper.h:113
#define HT_END
Definition: Logger.h:220
#define HT_ERROR_OUT
Definition: Logger.h:301
void rename(const std::string &old_name, const std::string &new_name)
Rename one entity, it doesn't recursively rename all entities under the path specified by old_name...
Hypertable definitions
static void get_namespace_listing(const std::vector< Hyperspace::DirEntryAttr > &dir_listing, std::vector< NamespaceListing > &listing)
uint8_t * base
Pointer to the allocated memory buffer.
This is a generic exception class for Hypertable.
Definition: Error.h:314
Holds extended attribute and value.
Definition: Protocol.h:48
bool name_to_id(const std::string &name, std::string &id, bool *is_namespacep=0)
Definition: NameIdMapper.cc:64
Create file if it does not exist.
Definition: Session.h:77
Open file for reading.
Definition: Session.h:71
size_t size() const
Returns the number of characters written to the output buffer.
Definition: String.h:156
String extensions and helpers: sets, maps, append operators etc.
Error codes, Exception handling, error logging.
#define HT_THROW(_code_, _msg_)
Definition: Error.h:478
NameIdMapper(Hyperspace::SessionPtr &hyperspace, const std::string &toplevel_dir)
Definition: NameIdMapper.cc:40
const char * c_str() const
Returns a pointer to the output buffer content with terminating null character appended.
Definition: String.h:168
#define HT_DEBUG_OUT
Definition: Logger.h:261
int code() const
Returns the error code.
Definition: Error.h:391
Executes user-defined functions when leaving the current scope.
Definition: DirEntryAttr.h:71