CernVM-FS  2.12.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) {
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, '/');
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  // Split remaining path into its parts
151  std::vector<std::string> path_parts = SplitString(line, '/');
152  cur_node = node_cache.top()->node;
153  // Store second last mode to check whether it is NOT passthrough!
154  char past_1_mode = node_cache.top()->node->mode;
155  char past_2_mode = node_cache.top()->node->mode;
156  for (std::vector<std::string>::const_iterator part_it = path_parts.begin();
157  part_it != path_parts.end();
158  part_it++) {
159  if (*part_it == "") { // If path is fully parsed/empty part => continue
160  continue;
161  }
162  if ((cur_node = node_cache.top()->node->GetNode(*part_it)) == NULL) {
163  cur_node = new SpecTreeNode(passthrough_mode);
164  node_cache.top()->node->AddNode(*part_it, cur_node);
165  }
166  entr = new struct NodeCacheEntry;
167  entr->node = cur_node;
168  entr->path = node_cache.top()->path + *part_it + "/";
169  node_cache.push(entr);
170  past_1_mode = past_2_mode;
171  past_2_mode = passthrough_mode;
172  }
173  cur_node->mode = inclusion_mode;
174  if (inclusion_mode != '!' && past_1_mode == '-') {
175  node_cache.pop();
176  while (!node_cache.empty() && node_cache.top()->node->mode == '-') {
177  node_cache.top()->node->mode = '_';
178  node_backup.push(node_cache.top());
179  node_cache.pop();
180  }
181  while (!node_backup.empty()) {
182  node_cache.push(node_backup.top());
183  node_cache.pop();
184  }
185  node_cache.push(entr);
186  }
187  }
188  while (!node_cache.empty()) {
189  entr = node_cache.top();
190  delete entr;
191  node_cache.pop();
192  }
193 }
194 
195 int SpecTree::ListDir(const char *dir,
196  char ***buf,
197  size_t *len) {
198  std::string path = dir;
199  SpecTreeNode *cur_node = root_;
200  bool is_wildcard = (cur_node->mode == '*');
201  bool is_flat_cp = (cur_node->mode == '^');
202  if (cur_node->mode == '!') {
203  return -1;
204  }
205  if (path.length() > 0 && path.at(path.length()-1) == '/') {
206  path.erase(path.length()-1);
207  }
208  std::vector<std::string> path_parts = SplitString(path, '/');
209  for (std::vector<std::string>::const_iterator part_it = 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,
248  char ***buf, size_t *len) {
249  *len = 0;
250  size_t buflen = 5;
251  *buf = reinterpret_cast<char **>(smalloc(sizeof(char *) * buflen));
252  // NULL terminate the list;
253  AppendStringToList(NULL, buf, len, &buflen);
254  for (std::map<std::string, SpecTreeNode*>::const_iterator
255  it = nodes_.begin();
256  it != nodes_.end();
257  it++) {
258  // Do not follow excluded paths and paths only existing for exclusion
259  if (it->second->mode != '!' && it->second->mode != '-') {
260  AppendStringToList(it->first.c_str(), buf, len, &buflen);
261  }
262  }
263  return 0; // nodes_.begin();
264 }
Definition: spec_tree.cc:27
#define PANIC(...)
Definition: exception.h:29
string Trim(const string &raw, bool trim_newline)
Definition: string.cc:446
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:802
bool GetLineFile(FILE *f, std::string *line)
Definition: string.cc:404
SpecTreeNode * root_
Definition: spec_tree.h:84
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:308
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:285
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:243
int ListDir(const char *dir, char ***buf, size_t *len)
Definition: spec_tree.cc:195
void AppendStringToList(char const *str, char ***buf, size_t *listlen, size_t *buflen)
Definition: util.cc:64