CernVM-FS  2.13.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 "util.h"
14 #include "util/exception.h"
15 #include "util/logging.h"
16 #include "util/posix.h"
17 #include "util/smalloc.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) {
39  PANIC(kLogStderr, "Cannot open specfile for reading at '%s' (errno: %d)",
40  path.c_str(), errno);
41  }
42  Parse(spec_file);
43  fclose(spec_file);
44 }
45 
46 bool SpecTree::IsMatching(std::string path) {
47  if (path.length() == 0) {
48  path = "/";
49  }
50  SpecTreeNode *cur_node = root_;
51  bool is_wildcard = (cur_node->mode == '*');
52  bool is_flat_cp = (cur_node->mode == '^');
53  if (cur_node->mode == '!') {
54  return false;
55  }
56  if (path.length() > 0 && path.at(path.length() - 1) == '/') {
57  path.erase(path.length() - 1);
58  }
59  std::vector<std::string> path_parts = SplitString(path, '/');
60  for (std::vector<std::string>::const_iterator part_it =
61  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 || cur_node->mode == '_'
83  || 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 == '!')
150  passthrough_mode = '-';
151  // Split remaining path into its parts
152  std::vector<std::string> path_parts = SplitString(line, '/');
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, char ***buf, size_t *len) {
197  std::string path = dir;
198  SpecTreeNode *cur_node = root_;
199  bool is_wildcard = (cur_node->mode == '*');
200  bool is_flat_cp = (cur_node->mode == '^');
201  if (cur_node->mode == '!') {
202  return -1;
203  }
204  if (path.length() > 0 && path.at(path.length() - 1) == '/') {
205  path.erase(path.length() - 1);
206  }
207  std::vector<std::string> path_parts = SplitString(path, '/');
208  for (std::vector<std::string>::const_iterator part_it =
209  path_parts.begin() + 1;
210  part_it != path_parts.end();
211  part_it++) {
212  cur_node = cur_node->GetNode(*part_it);
213  if (cur_node == NULL) {
214  if (is_wildcard) {
215  break;
216  }
217  return -1;
218  }
219  is_flat_cp = false;
220  if (cur_node->mode == '!') {
221  return -1;
222  }
223  if (cur_node->mode == '*') {
224  is_wildcard = true;
225  }
226  if (cur_node->mode == '^') {
227  is_flat_cp = true;
228  }
229  }
230  if (is_wildcard || is_flat_cp) {
231  return SPEC_READ_FS;
232  }
233  return cur_node->GetListing(path, buf, len);
234 }
235 
236 SpecTreeNode *SpecTreeNode::GetNode(const std::string &name) {
237  if (nodes_.count(name) == 0) {
238  return NULL;
239  }
240  return nodes_[name];
241 }
242 
243 void SpecTreeNode::AddNode(const std::string &name, SpecTreeNode *node) {
244  nodes_[name] = node;
245 }
246 
247 int SpecTreeNode::GetListing(std::string base_path, char ***buf, size_t *len) {
248  *len = 0;
249  size_t buflen = 5;
250  *buf = reinterpret_cast<char **>(smalloc(sizeof(char *) * buflen));
251  // NULL terminate the list;
252  AppendStringToList(NULL, buf, len, &buflen);
253  for (std::map<std::string, SpecTreeNode *>::const_iterator it =
254  nodes_.begin();
255  it != nodes_.end();
256  it++) {
257  // Do not follow excluded paths and paths only existing for exclusion
258  if (it->second->mode != '!' && it->second->mode != '-') {
259  AppendStringToList(it->first.c_str(), buf, len, &buflen);
260  }
261  }
262  return 0; // nodes_.begin();
263 }
Definition: spec_tree.cc:27
#define PANIC(...)
Definition: exception.h:29
string Trim(const string &raw, bool trim_newline)
Definition: string.cc:466
static SpecTree * Create(const std::string &path)
Definition: spec_tree.cc:21
void Open(const std::string &path)
Definition: spec_tree.cc:32
std::map< std::string, SpecTreeNode * > nodes_
Definition: spec_tree.h:43
SpecTreeNode * node
Definition: spec_tree.cc:28
bool FileExists(const std::string &path)
Definition: posix.cc:803
bool GetLineFile(FILE *f, std::string *line)
Definition: string.cc:422
SpecTreeNode * root_
Definition: spec_tree.h:75
int GetListing(std::string base_path, char ***buf, size_t *len)
Definition: spec_tree.cc:247
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:306
std::string path
Definition: spec_tree.cc:29
SpecTreeNode * GetNode(const std::string &name)
Definition: spec_tree.cc:236
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:279
void Parse(FILE *spec_file)
Definition: spec_tree.cc:86
SpecTree(char mode=0)
Definition: spec_tree.h:48
bool IsMatching(std::string path)
Definition: spec_tree.cc:46
void AddNode(const std::string &name, SpecTreeNode *node)
Definition: spec_tree.cc:243
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:61