monotone

monotone Mtn Source Tree

Root/src/restrictions.cc

1// Copyright (C) 2005 Derek Scherger <derek@echologic.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include "safe_map.hh"
12#include "vector.hh"
13#include "restrictions.hh"
14#include "file_io.hh"
15#include "roster.hh"
16#include "database.hh" // for parent_roster
17
18using std::make_pair;
19using std::map;
20using std::set;
21using std::vector;
22
23// TODO: add check for relevant rosters to be used by log
24//
25// i.e. as log goes back through older and older rosters it may hit one
26// that pre-dates any of the nodes in the restriction. the nodes that the
27// restriction includes or excludes may not have been born in a sufficiently
28// old roster. at this point log should stop because no earlier roster will
29// include these nodes.
30
31typedef map<node_id, restricted_path::status>::const_iterator
32 node_status_iterator;
33
34typedef map<file_path, restricted_path::status>::const_iterator
35 path_status_iterator;
36
37typedef set<file_path>::const_iterator path_iterator;
38
39static void
40add_parents(map<node_id, restricted_path::status> & node_map,
41 roster_t const & roster)
42{
43 set<node_id> parents;
44
45 // accumulate the set of parents of all included nodes
46 for (node_status_iterator i = node_map.begin(); i != node_map.end(); ++i)
47 {
48 if (i->second == restricted_path::included && roster.has_node(i->first))
49 {
50 node_id parent = roster.get_node(i->first)->parent;
51 while (!null_node(parent) && parents.find(parent) == parents.end())
52 {
53 parents.insert(parent);
54 parent = roster.get_node(parent)->parent;
55 }
56 }
57 }
58
59 // add implicit includes for all required parents
60 for (set<node_id>::const_iterator i = parents.begin();
61 i != parents.end(); ++i)
62 {
63 node_status_iterator n = node_map.find(*i);
64 if (n == node_map.end())
65 {
66 file_path fp;
67 roster.get_name(*i, fp);
68 L(FL("including missing parent '%s'") % fp);
69 node_map[*i] = restricted_path::required;
70 }
71 else if (n->second == restricted_path::included)
72 {
73 node_map[*i] = restricted_path::included_required;
74 }
75 else if (n->second == restricted_path::excluded)
76 {
77 file_path fp;
78 roster.get_name(*i, fp);
79 W(F("including required parent '%s'") % fp);
80 node_map[*i] = restricted_path::excluded_required;
81 }
82 }
83}
84
85static void
86map_nodes(map<node_id, restricted_path::status> & node_map,
87 roster_t const & roster,
88 set<file_path> const & paths,
89 set<file_path> & known_paths,
90 restricted_path::status const status)
91{
92 for (path_iterator i = paths.begin(); i != paths.end(); ++i)
93 {
94 if (roster.has_node(*i))
95 {
96 known_paths.insert(*i);
97 node_id nid = roster.get_node(*i)->self;
98
99 node_status_iterator n = node_map.find(nid);
100 if (n != node_map.end())
101 E(n->second == status, origin::user,
102 F("conflicting include/exclude on path '%s'") % *i);
103 else
104 node_map[nid] = status;
105
106 }
107 }
108}
109
110static void
111map_nodes(map<node_id, restricted_path::status> & node_map,
112 roster_t const & roster,
113 set<file_path> const & included_paths,
114 set<file_path> const & excluded_paths,
115 set<file_path> & known_paths)
116{
117 map_nodes(node_map, roster, included_paths, known_paths,
118 restricted_path::included);
119 map_nodes(node_map, roster, excluded_paths, known_paths,
120 restricted_path::excluded);
121}
122
123static void
124add_parents(map<file_path, restricted_path::status> & path_map)
125{
126 set<file_path> parents;
127
128 // accumulate the set of parents of all included paths
129 for (path_status_iterator i = path_map.begin(); i != path_map.end(); ++i)
130 {
131 if (i->second == restricted_path::included)
132 {
133 file_path parent = i->first.dirname();
134 while (!parent.empty() && parents.find(parent) == parents.end())
135 {
136 parents.insert(parent);
137 parent = parent.dirname();
138 }
139 parents.insert(parent);
140 }
141 }
142
143 // add implicit includes for all required parents
144 for (set<file_path>::const_iterator i = parents.begin();
145 i != parents.end(); ++i)
146 {
147 path_status_iterator p = path_map.find(*i);
148 if (p == path_map.end())
149 {
150 L(FL("including missing parent '%s'") % *i);
151 path_map[*i] = restricted_path::required;
152 }
153 else if (p->second == restricted_path::included)
154 {
155 path_map[*i] = restricted_path::included_required;
156 }
157 else if (p->second == restricted_path::excluded)
158 {
159 W(F("including required parent '%s'") % *i);
160 path_map[*i] = restricted_path::excluded_required;
161 }
162 }
163
164}
165
166static void
167map_paths(map<file_path, restricted_path::status> & path_map,
168 set<file_path> const & paths,
169 restricted_path::status const status)
170{
171 for (path_iterator i = paths.begin(); i != paths.end(); ++i)
172 {
173 path_status_iterator p = path_map.find(*i);
174 if (p != path_map.end())
175 E(p->second == status, origin::user,
176 F("conflicting include/exclude on path '%s'") % *i);
177 else
178 path_map[*i] = status;
179 }
180}
181
182namespace
183{
184 // ignored paths are allowed into the restriction but are not considered
185 // invalid if they are found in none of the restriction's rosters
186 struct unknown_unignored_node : public path_predicate<file_path>
187 {
188 explicit unknown_unignored_node(set<file_path> const & known_paths,
189 path_predicate<file_path> const & ignore)
190 : known_paths(known_paths), ignore_file(ignore)
191 {}
192 virtual bool operator()(file_path const & p) const
193 {
194 return (known_paths.find(p) == known_paths.end() && !ignore_file(p));
195 }
196
197 private:
198 set<file_path> const & known_paths;
199 path_predicate<file_path> const & ignore_file;
200 };
201
202 struct unknown_unignored_path : public path_predicate<file_path>
203 {
204 explicit unknown_unignored_path(path_predicate<file_path> const & ignore)
205 : ignore_file(ignore)
206 {}
207 virtual bool operator()(file_path const & p) const
208 {
209 return !path_exists(p) && !ignore_file(p);
210 }
211 private:
212 path_predicate<file_path> const & ignore_file;
213 };
214}
215
216static void
217validate_paths(set<file_path> const & included_paths,
218 set<file_path> const & excluded_paths,
219 path_predicate<file_path> const & is_unknown)
220{
221 int bad = 0;
222
223 for (path_iterator i = included_paths.begin(); i != included_paths.end(); ++i)
224 if (is_unknown(*i))
225 {
226 bad++;
227 W(F("restriction includes unknown path '%s'") % *i);
228 }
229
230 for (path_iterator i = excluded_paths.begin(); i != excluded_paths.end(); ++i)
231 if (is_unknown(*i))
232 {
233 bad++;
234 W(F("restriction excludes unknown path '%s'") % *i);
235 }
236
237 E(bad == 0, origin::user,
238 FP("%d unknown path", "%d unknown paths", bad) % bad);
239}
240
241restriction::restriction(vector<file_path> const & includes,
242 vector<file_path> const & excludes,
243 long depth)
244 : included_paths(includes.begin(), includes.end()),
245 excluded_paths(excludes.begin(), excludes.end()),
246 depth(depth)
247{}
248
249node_restriction::node_restriction(vector<file_path> const & includes,
250 vector<file_path> const & excludes,
251 long depth,
252 roster_t const & roster,
253 path_predicate<file_path> const & ignore,
254 include_rules const & rules)
255 : restriction(includes, excludes, depth)
256{
257 map_nodes(node_map, roster, included_paths, excluded_paths, known_paths);
258
259 if (rules == implicit_includes)
260 add_parents(node_map, roster);
261
262 validate_paths(included_paths, excluded_paths,
263 unknown_unignored_node(known_paths, ignore));
264}
265
266node_restriction::node_restriction(vector<file_path> const & includes,
267 vector<file_path> const & excludes,
268 long depth,
269 roster_t const & roster1,
270 roster_t const & roster2,
271 path_predicate<file_path> const & ignore,
272 include_rules const & rules)
273 : restriction(includes, excludes, depth)
274{
275 map_nodes(node_map, roster1, included_paths, excluded_paths, known_paths);
276 map_nodes(node_map, roster2, included_paths, excluded_paths, known_paths);
277
278 if (rules == implicit_includes)
279 {
280 add_parents(node_map, roster1);
281 add_parents(node_map, roster2);
282 }
283
284 validate_paths(included_paths, excluded_paths,
285 unknown_unignored_node(known_paths, ignore));
286}
287
288node_restriction::node_restriction(vector<file_path> const & includes,
289 vector<file_path> const & excludes,
290 long depth,
291 parent_map const & rosters1,
292 roster_t const & roster2,
293 path_predicate<file_path> const & ignore,
294 include_rules const & rules)
295 : restriction(includes, excludes, depth)
296{
297 for (parent_map::const_iterator i = rosters1.begin();
298 i != rosters1.end(); i++)
299 map_nodes(node_map, parent_roster(i),
300 included_paths, excluded_paths, known_paths);
301 map_nodes(node_map, roster2, included_paths, excluded_paths, known_paths);
302
303 if (rules == implicit_includes)
304 {
305 for (parent_map::const_iterator i = rosters1.begin();
306 i != rosters1.end(); i++)
307 add_parents(node_map, parent_roster(i));
308 add_parents(node_map, roster2);
309 }
310
311 validate_paths(included_paths, excluded_paths,
312 unknown_unignored_node(known_paths, ignore));
313}
314
315path_restriction::path_restriction(vector<file_path> const & includes,
316 vector<file_path> const & excludes,
317 long depth,
318 path_predicate<file_path> const & ignore)
319 : restriction(includes, excludes, depth)
320{
321 map_paths(path_map, included_paths, restricted_path::included);
322 map_paths(path_map, excluded_paths, restricted_path::excluded);
323 add_parents(path_map);
324 validate_paths(included_paths, excluded_paths,
325 unknown_unignored_path(ignore));
326}
327
328path_restriction::path_restriction(vector<file_path> const & includes,
329 vector<file_path> const & excludes,
330 long depth,
331 path_restriction::skip_check_t)
332 : restriction(includes, excludes, depth)
333{
334 map_paths(path_map, included_paths, restricted_path::included);
335 map_paths(path_map, excluded_paths, restricted_path::excluded);
336 add_parents(path_map);
337}
338
339bool
340node_restriction::includes(roster_t const & roster, node_id nid) const
341{
342 MM(roster);
343 I(roster.has_node(nid));
344
345 file_path fp;
346 roster.get_name(nid, fp);
347
348 if (empty())
349 {
350 if (depth != -1)
351 {
352 int path_depth = fp.depth();
353 if (path_depth <= depth)
354 {
355 L(FL("depth includes nid %d path '%s'") % nid % fp);
356 return true;
357 }
358 else
359 {
360 L(FL("depth excludes nid %d path '%s'") % nid % fp);
361 return false;
362 }
363 }
364 else
365 {
366 // don't log this, we end up using rather a bit of cpu time just
367 // in the logging code, for totally unrestricted operations.
368 return true;
369 }
370 }
371
372 node_id current = nid;
373 int path_depth = 0;
374
375 while (!null_node(current) && (depth == -1 || path_depth <= depth))
376 {
377 node_status_iterator r = node_map.find(current);
378
379 if (r != node_map.end())
380 {
381 switch (r->second)
382 {
383 case restricted_path::included:
384 case restricted_path::included_required:
385 L(FL("explicit include of nid %d path '%s'") % current % fp);
386 return true;
387
388 case restricted_path::excluded:
389 L(FL("explicit exclude of nid %d path '%s'") % current % fp);
390 return false;
391
392 case restricted_path::required:
393 case restricted_path::excluded_required:
394 if (path_depth == 0)
395 {
396 L(FL("implicit include of nid %d path '%s'") % current % fp);
397 return true;
398 }
399 else
400 {
401 return false;
402 }
403 }
404 }
405
406 const_node_t node = roster.get_node(current);
407 current = node->parent;
408 path_depth++;
409 }
410
411 if (included_paths.empty())
412 {
413 L(FL("default include of nid %d path '%s'")
414 % nid % fp);
415 return true;
416 }
417 else
418 {
419 L(FL("(debug) default exclude of nid %d path '%s'")
420 % nid % fp);
421 return false;
422 }
423}
424
425bool
426path_restriction::includes(file_path const & pth) const
427{
428 if (empty())
429 {
430 if (depth != -1)
431 {
432 int path_depth = pth.depth();
433 if (path_depth <= depth)
434 {
435 L(FL("depth includes path '%s'") % pth);
436 return true;
437 }
438 else
439 {
440 L(FL("depth excludes path '%s'") % pth);
441 return false;
442 }
443 }
444 else
445 {
446 L(FL("empty include of path '%s'") % pth);
447 return true;
448 }
449 }
450
451 int path_depth = 0;
452 file_path fp = pth;
453 while (depth == -1 || path_depth <= depth)
454 {
455 path_status_iterator r = path_map.find(fp);
456
457 if (r != path_map.end())
458 {
459 switch (r->second)
460 {
461 case restricted_path::included:
462 case restricted_path::included_required:
463 L(FL("explicit include of path '%s'") % pth);
464 return true;
465
466 case restricted_path::excluded:
467 L(FL("explicit exclude of path '%s'") % pth);
468 return false;
469
470 case restricted_path::required:
471 case restricted_path::excluded_required:
472 if (path_depth == 0)
473 {
474 L(FL("implicit include of path '%s'") % pth);
475 return true;
476 }
477 else
478 {
479 return false;
480 }
481 }
482 }
483
484 if (fp.empty())
485 break;
486 fp = fp.dirname();
487 path_depth++;
488 }
489
490 if (included_paths.empty())
491 {
492 L(FL("default include of path '%s'") % pth);
493 return true;
494 }
495 else
496 {
497 L(FL("default exclude of path '%s'") % pth);
498 return false;
499 }
500}
501
502// Local Variables:
503// mode: C++
504// fill-column: 76
505// c-file-style: "gnu"
506// indent-tabs-mode: nil
507// End:
508// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status