CernVM-FS  2.9.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
spec_tree.cc
Go to the documentation of this file.
1 
4 #include "spec_tree.h"
5 
6 #include <errno.h>
7 
8 #include <map>
9 #include <stack>
10 #include <string>
11 #include <vector>
12 
13 #include "logging.h"
14 #include "smalloc.h"
15 #include "util.h"
16 #include "util/exception.h"
17 #include "util/posix.h"
18 #include "util/string.h"
19 
20 
21 SpecTree *SpecTree::Create(const std::string &path) {
22  SpecTree *res = new SpecTree();
23  res->Open(path);
24  return res;
25  }
26 
29  std::string path;
30 };
31 
32 void SpecTree::Open(const std::string &path) {
33  if (!FileExists(path)) {
34  PANIC(kLogStderr, "Cannot find specfile at '%s'", path.c_str());
35  }
36 
37  FILE *spec_file = fopen(path.c_str(), "r");
38  if (spec_file == NULL) {
40  "Cannot open specfile for reading at '%s' (errno: %d)",
41  path.c_str(), errno);
42  }
43  Parse(spec_file);
44  fclose(spec_file);
45 }
46 
47 bool SpecTree::IsMatching(std::string path) {
48  if (path.length() == 0) {
49  path = "/";
50  }
51  SpecTreeNode *cur_node = root_;
52  bool is_wildcard = (cur_node->mode == '*');
53  bool is_flat_cp = (cur_node->mode == '^');
54  if (cur_node->mode == '!') {
55  return false;
56  }
57  if (path.length() > 0 && path.at(path.length()-1) == '/') {
58  path.erase(path.length()-1);
59  }
60  std::vector<std::string> path_parts = SplitString(path, '/', 256);
61  for (std::vector<std::string>::const_iterator part_it = path_parts.begin()+1;
62  part_it != path_parts.end();
63  part_it++) {
64  cur_node = cur_node->GetNode(*part_it);
65  if (cur_node == NULL) {
66  if (is_wildcard || (is_flat_cp && (part_it+1) == path_parts.end())) {
67  return true;
68  }
69  return false;
70  }
71  is_flat_cp = false;
72  if (cur_node->mode == '!') {
73  return false;
74  }
75  if (cur_node->mode == '*') {
76  is_wildcard = true;
77  }
78  if (cur_node->mode == '^') {
79  is_flat_cp = true;
80  }
81  }
82  return is_wildcard || is_flat_cp
83  || cur_node->mode == '_' || cur_node->mode == 0;
84 }
85 
86 void SpecTree::Parse(FILE *spec_file) {
87  // Slash terminated paths en route to the last treated spec line
88  /*
89  * This stack is used to speed up the parsing of (partially) sorted spec files
90  * The node_backup stack is used in cases where NOT passthrough entries need
91  * to be rewritten to passthrough entries by temporarily storing the
92  * node_cache entries in node_backup while looking for all nodes that need to
93  * be changed
94  */
95  std::stack<NodeCacheEntry *> node_cache;
96  std::stack<NodeCacheEntry *> node_backup;
97  // Root entry describing the root of the tree
98  struct NodeCacheEntry *root = new struct NodeCacheEntry;
99  root->node = root_;
100  root->path = "/";
101  node_cache.push(root);
102  std::string line;
103  // The mode for inclusion (see spec_tree.h) used for the current spec line
104  char inclusion_mode;
105  // Temporary storage for entries
106  NodeCacheEntry *entr;
107  SpecTreeNode *cur_node;
108  // Whether there is a path on the stack disallowing inclusion
109  while (GetLineFile(spec_file, &line)) { // Go through spec file lines
110  std::string raw_line = line;
111  line = Trim(line);
112  if (line.empty() || line[0] == '#')
113  continue;
114  if ((line[0] != '/') && (line[0] != '!') && (line[0] != '^'))
115  PANIC(kLogStderr, "Invalid specification: %s", raw_line.c_str());
116 
117  // FIND inclusion_mode (START)
118  inclusion_mode = 0;
119  if (line.at(0) == '^' || line.at(0) == '!') {
120  inclusion_mode = line.at(0);
121  line.erase(0, 1);
122  }
123  if (line.empty())
124  PANIC(kLogStderr, "Invalid specification: %s", raw_line.c_str());
125  if (line.at(line.length()-1) == '*') {
126  if (inclusion_mode == 0) {
127  inclusion_mode = '*';
128  }
129  line.erase(line.length()-1);
130  } else if (inclusion_mode == '^') {
131  inclusion_mode = 0;
132  }
133  if (line.empty() || (line[0] != '/'))
134  PANIC(kLogStderr, "Invalid specification: %s", raw_line.c_str());
135  // FIND inclusion_mode (END)
136  while (!node_cache.empty()) { // Find nearest parent node in node cache
137  entr = node_cache.top();
138  if (HasPrefix(line, entr->path, false /*ignore_case*/)) {
139  // If entr->path is prefix => parent
140  line.erase(0, entr->path.length());
141  break;
142  } else { // If no longer parent of current element
143  // => drop cache entry for now
144  node_cache.pop();
145  delete entr;
146  }
147  }
148  char passthrough_mode = '_';
149  if (inclusion_mode == '!') passthrough_mode = '-';
150  // TODO(steuber): max_chunks?
151  // Split remaining path into its parts
152  std::vector<std::string> path_parts = SplitString(line, '/', 256);
153  cur_node = node_cache.top()->node;
154  // Store second last mode to check whether it is NOT passthrough!
155  char past_1_mode = node_cache.top()->node->mode;
156  char past_2_mode = node_cache.top()->node->mode;
157  for (std::vector<std::string>::const_iterator part_it = path_parts.begin();
158  part_it != path_parts.end();
159  part_it++) {
160  if (*part_it == "") { // If path is fully parsed/empty part => continue
161  continue;
162  }
163  if ((cur_node = node_cache.top()->node->GetNode(*part_it)) == NULL) {
164  cur_node = new SpecTreeNode(passthrough_mode);
165  node_cache.top()->node->AddNode(*part_it, cur_node);
166  }
167  entr = new struct NodeCacheEntry;
168  entr->node = cur_node;
169  entr->path = node_cache.top()->path + *part_it + "/";
170  node_cache.push(entr);
171  past_1_mode = past_2_mode;
172  past_2_mode = passthrough_mode;
173  }
174  cur_node->mode = inclusion_mode;
175  if (inclusion_mode != '!' && past_1_mode == '-') {
176  node_cache.pop();
177  while (!node_cache.empty() && node_cache.top()->node->mode == '-') {
178  node_cache.top()->node->mode = '_';
179  node_backup.push(node_cache.top());
180  node_cache.pop();
181  }
182  while (!node_backup.empty()) {
183  node_cache.push(node_backup.top());
184  node_cache.pop();
185  }
186  node_cache.push(entr);
187  }
188  }
189  while (!node_cache.empty()) {
190  entr = node_cache.top();
191  delete entr;
192  node_cache.pop();
193  }
194 }
195 
196 int SpecTree::ListDir(const char *dir,
197  char ***buf,
198  size_t *len) {
199  std::string path = dir;
200  SpecTreeNode *cur_node = root_;
201  bool is_wildcard = (cur_node->mode == '*');
202  bool is_flat_cp = (cur_node->mode == '^');
203  if (cur_node->mode == '!') {
204  return -1;
205  }
206  if (path.length() > 0 && path.at(path.length()-1) == '/') {
207  path.erase(path.length()-1);
208  }
209  std::vector<std::string> path_parts = SplitString(path, '/', 256);
210  for (std::vector<std::string>::const_iterator part_it = path_parts.begin()+1;
211  part_it != path_parts.end();
212  part_it++) {
213  cur_node = cur_node->GetNode(*part_it);
214  if (cur_node == NULL) {
215  if (is_wildcard) {
216  break;
217  }
218  return -1;
219  }
220  is_flat_cp = false;
221  if (cur_node->mode == '!') {
222  return -1;
223  }
224  if (cur_node->mode == '*') {
225  is_wildcard = true;
226  }
227  if (cur_node->mode == '^') {
228  is_flat_cp = true;
229  }
230  }
231  if (is_wildcard || is_flat_cp) {
232  return SPEC_READ_FS;
233  }
234  return cur_node->GetListing(path, buf, len);
235 }
236 
237 SpecTreeNode *SpecTreeNode::GetNode(const std::string &name) {
238  if (nodes_.count(name) == 0) {
239  return NULL;
240  }
241  return nodes_[name];
242 }
243 
244 void SpecTreeNode::AddNode(const std::string &name, SpecTreeNode *node) {
245  nodes_[name] = node;
246 }
247 
248 int SpecTreeNode::GetListing(std::string base_path,
249  char ***buf, size_t *len) {
250  *len = 0;
251  size_t buflen = 5;
252  *buf = reinterpret_cast<char **>(smalloc(sizeof(char *) * buflen));
253  // NULL terminate the list;
254  AppendStringToList(NULL, buf, len, &buflen);
255  for (std::map<std::string, SpecTreeNode*>::const_iterator
256  it = nodes_.begin();
257  it != nodes_.end();
258  it++) {
259  // Do not follow excluded paths and paths only existing for exclusion
260  if (it->second->mode != '!' && it->second->mode != '-') {
261  AppendStringToList(it->first.c_str(), buf, len, &buflen);
262  }
263  }
264  return 0; // nodes_.begin();
265 }
Definition: spec_tree.cc:27
vector< string > SplitString(const string &str, const char delim, const unsigned max_chunks)
Definition: string.cc:288
#define PANIC(...)
Definition: exception.h:26
string Trim(const string &raw, bool trim_newline)
Definition: string.cc:421
static SpecTree * Create(const std::string &path)
Definition: spec_tree.cc:21
void Open(const std::string &path)
Definition: spec_tree.cc:32
SpecTreeNode * node
Definition: spec_tree.cc:28
bool FileExists(const std::string &path)
Definition: posix.cc:816
bool GetLineFile(FILE *f, std::string *line)
Definition: string.cc:379
SpecTreeNode * root_
Definition: spec_tree.h:84
int GetListing(std::string base_path, char ***buf, size_t *len)
Definition: spec_tree.cc:248
std::string path
Definition: spec_tree.cc:29
SpecTreeNode * GetNode(const std::string &name)
Definition: spec_tree.cc:237
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:265
std::map< std::string, SpecTreeNode * > nodes_
Definition: spec_tree.h:44
void Parse(FILE *spec_file)
Definition: spec_tree.cc:86
SpecTree(char mode=0)
Definition: spec_tree.h:49
bool IsMatching(std::string path)
Definition: spec_tree.cc:47
void AddNode(const std::string &name, SpecTreeNode *node)
Definition: spec_tree.cc:244
int ListDir(const char *dir, char ***buf, size_t *len)
Definition: spec_tree.cc:196
void AppendStringToList(char const *str, char ***buf, size_t *listlen, size_t *buflen)
Definition: util.cc:64