clsync
rules.c
Go to the documentation of this file.
1 /*
2  clsync - file tree sync utility based on inotify/kqueue
3 
4  Copyright (C) 2013-2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program 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, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "common.h"
21 
22 #include <glib.h> // g_hash_table_*
23 
24 #include "rules.h"
25 #include "error.h"
26 
27 int rule_complete ( rule_t *rule_p, char *expr, size_t *rules_count_p )
28 {
29  debug ( 3, "<%s>.", expr );
30 #ifdef VERYPARANOID
31 
32  if ( rule_p->mask == RA_NONE ) {
33  error ( "Received a rule with rule_p->mask == 0x00. Exit." );
34  return EINVAL;
35  }
36 
37 #endif
38  char buf[BUFSIZ];
39  int ret = 0;
40 
41  if ( rule_p->num >= MAXRULES ) {
42  error ( "Too many rules (%i >= %i).", rule_p->num, MAXRULES );
43  return ENOMEM;
44  }
45 
46  if ( ( ret = regcomp ( &rule_p->expr, expr, REG_EXTENDED | REG_NOSUB ) ) ) {
47  regerror ( ret, &rule_p->expr, buf, BUFSIZ );
48  error ( "Invalid regexp pattern <%s>: %s (regex-errno: %i).", expr, buf, ret );
49  return ret;
50  }
51 
52  ( *rules_count_p )++;
53  return ret;
54 }
55 
57 {
58  int ret = 0;
59  char *rulfpath = ctx_p->rulfpath;
60  rule_t *rules = ctx_p->rules;
61  size_t *rules_count_p = &ctx_p->rules_count;
62  char *line_buf = NULL;
63  FILE *f = fopen ( rulfpath, "r" );
64 
65  if ( f == NULL ) {
66  rules->mask = RA_NONE; // Terminator. End of rules' chain.
67  rules->perm = DEFAULT_RULES_PERM;
68  error ( "Cannot open \"%s\" for reading.", rulfpath );
69  return errno;
70  }
71 
72  GHashTable *autowrules_ht = g_hash_table_new_full ( g_str_hash, g_str_equal, free, 0 );
73  int i = 0;
74  ssize_t linelen;
75  size_t size = 0;
76 
77  while ( ( linelen = getline ( &line_buf, &size, f ) ) != -1 ) {
78  if ( linelen > 1 ) {
79  uint8_t sign = 0;
80  char *line = line_buf;
81  rule_t *rule;
82  rule = &rules[i];
83 #ifdef VERYPARANOID
84  memset ( rule, 0, sizeof ( *rule ) );
85 #endif
86  rule->num = i++;
87  line[--linelen] = 0;
88 
89  // Parsing the first character of the line
90  switch ( *line ) {
91  case '+':
92  sign = RS_PERMIT;
93  break;
94 
95  case '-':
96  sign = RS_REJECT;
97  break;
98 
99  case '#': // Comment?
100  i--; // Canceling new rule
101  continue;
102 
103  default:
104  error ( "Wrong rule action <%c>.", *line );
105  return EINVAL;
106  }
107 
108  line++;
109  linelen--;
110  // Parsing the second character of the line
111  *line |= 0x20; // lower-casing
112  // Default rule->mask and rule->perm
113  // rule->mask - sets bitmask of operations that are affected by the rule
114  // rule->perm - sets bitmask of permit/reject for every operation. Effect have only bits specified by the rule->mask.
115  rule->mask = RA_ALL;
116 
117  switch ( sign ) {
118  case RS_REJECT:
119  rule->perm = RA_NONE;
120  break;
121 
122  case RS_PERMIT:
123  rule->perm = RA_ALL;
124  break;
125  }
126 
127  switch ( *line ) {
128  case '*':
129  rule->objtype = 0; // "0" - means "of any type"
130  break;
131 #ifdef DETAILED_FTYPE
132 
133  case 's':
134  rule->objtype = S_IFSOCK;
135  break;
136 
137  case 'l':
138  rule->objtype = S_IFLNK;
139  break;
140 
141  case 'b':
142  rule->objtype = S_IFBLK;
143  break;
144 
145  case 'c':
146  rule->objtype = S_IFCHR;
147  break;
148 
149  case 'p':
150  rule->objtype = S_IFIFO;
151  break;
152 #endif
153 
154  case 'f':
155  rule->objtype = S_IFREG;
156  break;
157 
158  case 'd':
159  rule->objtype = S_IFDIR;
160  break;
161 
162  case 'W':
163  case 'w':
164  case 'm':
165  case 's':
166  if (
167  ( ctx_p->flags[MODE] == MODE_RSYNCDIRECT ) ||
168  ( ctx_p->flags[MODE] == MODE_RSYNCSHELL ) ||
169  ( ctx_p->flags[MODE] == MODE_RSYNCSO )
170  )
171  warning ( "Used \"w\" or/and \"m\" or/and \"s\" rule in rsync \"--monitor\" case."
172  " This may cause unexpected problems." );
173 
174  rule->objtype = S_IFDIR;
175 
176  switch ( *line ) {
177  case 'W':
179  break;
180 
181  case 'w':
182  rule->mask = RA_WALK;
183  break;
184 
185  case 'm':
186  rule->mask = RA_MONITOR;
187  break;
188 
189  case 's':
190  rule->mask = RA_SYNC;
191  break;
192  }
193 
194  break;
195 
196  default:
197  warning ( "Cannot parse the rule <%s>", &line[-1] );
198  i--; // Canceling new rule
199  continue;
200  }
201 
202  line++;
203  linelen--;
204  // Parsing the rest part of the line
205  debug ( 1, "Rule #%i <%c>[0x%02x 0x%02x] <%c>[0x%04x] pattern <%s> (length: %i).", rule->num, line[-2], rule->perm, rule->mask, line[-1], rule->objtype, line, linelen );
206 
207  if ( ( ret = rule_complete ( rule, line, rules_count_p ) ) )
208  goto l_parse_rules_fromfile_end;
209 
210  // Post-processing:
211  line--;
212  linelen++;
213 #ifdef AUTORULESW
214 
215  if ( *line != 'w' ) {
216  // processing --auto-add-rules-w
217  if ( ctx_p->flags[AUTORULESW] && ( sign == RS_PERMIT ) ) {
218  // Preparing to add appropriate w-rules
219  char skip = 0;
220  char *expr = alloca ( linelen + 2 );
221  memcpy ( expr, line, linelen + 1 );
222  size_t exprlen = linelen;
223 
224  // Making expr to be starting with '^'
225  if ( line[1] == '^' ) {
226  expr++;
227  exprlen--;
228  } else
229  *expr = '^';
230 
231  char *end;
232 
233  if ( *line == 'd' || *line == '*' ) {
234  // "d" rule already doing what we need, so we can skip the last level
235  end = &expr[exprlen];
236 
237  if ( end[-1] != '$' )
238  * ( end++ ) = '$';
239 
240  *end = 0;
241 // debug(3, "Don't adding w-rule for \"%s\" due to [*d]-rule for \"%s\"",
242 // expr, &line[1]);
243  g_hash_table_insert ( autowrules_ht, strdup ( expr ), GINT_TO_POINTER ( 1 ) );
244  }
245 
246  if ( !skip ) {
247  do {
248  // Decreasing directory level and make the '$' ending
249  end = strrchr ( expr, '/' );
250 
251  if ( end != NULL ) {
252  if ( end[-1] != '$' )
253  * ( end++ ) = '$';
254 
255  *end = 0;
256  exprlen = ( size_t ) ( end - expr );
257  } else {
258  expr[1] = '$';
259  expr[2] = 0;
260  exprlen = 2;
261  }
262 
263  // Checking if it not already set
264  if ( !g_hash_table_lookup ( autowrules_ht, expr ) ) {
265  // Switching to next rule:
266  rule = &rules[i];
267  rule->num = i++;
268  // Adding the rule
269  rule->objtype = S_IFDIR;
270  rule->mask = RA_WALK;
271  rule->perm = RA_WALK;
272  debug ( 1, "Rule #%i <+> <w> pattern <%s> (length: %i) [auto].",
273  rule->num, expr, exprlen );
274 
275  if ( ( ret = rule_complete ( rule, expr, rules_count_p ) ) )
276  goto l_parse_rules_fromfile_end;
277 
278  g_hash_table_insert ( autowrules_ht, strdup ( expr ), GINT_TO_POINTER ( 1 ) );
279  }
280  } while ( end != NULL );
281  }
282  }
283  }
284 
285 #endif
286  }
287  }
288 
289 l_parse_rules_fromfile_end:
290 
291  if ( size )
292  free ( line_buf );
293 
294  fclose ( f );
295  debug ( 3, "Adding tail-rule #%u (effective #%u).", -1, i );
296  rules[i].mask = RA_NONE; // Terminator. End of rules' chain.
297  rules[i].perm = DEFAULT_RULES_PERM;
298  g_hash_table_destroy ( autowrules_ht );
299 #ifdef _DEBUG_FORCE
300  debug ( 3, "Total (p == %p):", rules );
301  i = 0;
302 
303  do {
304  debug ( 4, "\t%i\t%i\t%p/%p", i, rules[i].objtype, ( void * ) ( long ) rules[i].perm, ( void * ) ( long ) rules[i].mask );
305  i++;
306  } while ( rules[i].mask != RA_NONE );
307 
308 #endif
309  return ret;
310 }
311 
312 /**
313  * @brief Checks file path by rules' expressions (parsed from file)
314  *
315  * @param[in] fpath Path to file of directory
316  * @param[in] st_mode st_mode received via *stat() functions
317  * @param[in] rules_p Pointer to start of rules array
318  * @param[in] ruleaction Operaton ID (see ruleaction_t)
319  * @param[in,out] rule_pp Pointer to pointer to rule, where the last search ended. Next search will be started from the specified rule. Can be "NULL" to disable this feature.
320  *
321  * @retval perm Permission bitmask
322  *
323  */
324 // Checks file path by rules' expressions (parsed from file)
325 // Return: RS_PERMIT or RS_REJECT for the "file path" and specified ruleaction
326 
327 ruleaction_t rules_search_getperm ( const char *fpath, mode_t st_mode, rule_t *rules_p, const ruleaction_t ruleaction, rule_t **rule_pp )
328 {
329  debug ( 3, "rules_search_getperm(\"%s\", %p, %p, %p, %p)",
330  fpath, ( void * ) ( unsigned long ) st_mode, rules_p,
331  ( void * ) ( long ) ruleaction, ( void * ) ( long ) rule_pp
332  );
333  int i;
334  i = 0;
335  rule_t *rule_p = rules_p;
336  mode_t ftype = st_mode & S_IFMT;
337 #ifdef _DEBUG_FORCE
338  debug ( 3, "Rules (p == %p):", rules_p );
339  i = 0;
340 
341  do {
342  debug ( 3, "\t%i\t%i\t%p/%p", i, rules_p[i].objtype, ( void * ) ( long ) rules_p[i].perm, ( void * ) ( long ) rules_p[i].mask );
343  i++;
344  } while ( rules_p[i].mask != RA_NONE );
345 
346 #endif
347  i = 0;
348 
349  if ( rule_pp != NULL )
350  if ( *rule_pp != NULL ) {
351  debug ( 3, "Previous position is set." );
352 
353  if ( rule_p->mask == RA_NONE )
354  return rule_p->perm;
355 
356  rule_p = ++ ( *rule_pp );
357  i = rule_p->num;
358  }
359 
360  debug ( 3, "Starting from position %i", i );
361 
362  while ( rule_p->mask != RA_NONE ) {
363  debug ( 3, "%i -> %p/%p: type compare: %p, %p -> %i",
364  i,
365  ( void * ) ( long ) rule_p->perm, ( void * ) ( long ) rule_p->mask,
366  ( void * ) ( unsigned long ) ftype, ( void * ) ( unsigned long ) rule_p->objtype,
367  ( unsigned char ) ! ( rule_p->objtype && ( rule_p->objtype != ftype ) )
368  );
369 
370  if ( ! ( rule_p->mask & ruleaction ) ) { // Checking wrong operation type
371  debug ( 3, "action-mask mismatch. Skipping." );
372  rule_p++;
373  i++;// = &rules_p[++i];
374  continue;
375  }
376 
377  if ( rule_p->objtype && ( rule_p->objtype != ftype ) ) {
378  debug ( 3, "objtype mismatch. Skipping." );
379  rule_p++;
380  i++;// = &rules_p[++i];
381  continue;
382  }
383 
384  if ( !regexec ( &rule_p->expr, fpath, 0, NULL, 0 ) )
385  break;
386 
387  debug ( 3, "doesn't match regex. Skipping." );
388  rule_p++;
389  i++;// = &rules_p[++i];
390  }
391 
392  debug ( 2, "matched to rule #%u for \"%s\":\t%p/%p (queried: %p).", rule_p->mask == RA_NONE ? -1 : i, fpath,
393  ( void * ) ( long ) rule_p->perm, ( void * ) ( long ) rule_p->mask,
394  ( void * ) ( long ) ruleaction
395  );
396 
397  if ( rule_pp != NULL )
398  *rule_pp = rule_p;
399 
400  return rule_p->perm;
401 }
402 
403 ruleaction_t rules_getperm ( const char *fpath, mode_t st_mode, rule_t *rules_p, ruleaction_t ruleactions )
404 {
405  rule_t *rule_p = NULL;
406  ruleaction_t gotpermto = 0;
407  ruleaction_t resultperm = 0;
408  debug ( 3, "rules_getperm(\"%s\", %p, %p (#%u), %p)",
409  fpath, ( void * ) ( long ) st_mode, rules_p, rules_p->num, ( void * ) ( long ) ruleactions );
410 
411  while ( ( gotpermto & ruleactions ) != ruleactions ) {
412  rules_search_getperm ( fpath, st_mode, rules_p, ruleactions, &rule_p );
413 
414  if ( rule_p->mask == RA_NONE ) { // End of rules' list
415  resultperm |= rule_p->perm & ( gotpermto ^ RA_ALL );
416  break;
417  }
418 
419  resultperm |= rule_p->perm & ( ( gotpermto ^ rule_p->mask ) &rule_p->mask ); // Adding perm bitmask of operations that was unknown before
420  gotpermto |= rule_p->mask; // Adding the mask
421  }
422 
423  debug ( 3, "rules_getperm(\"%s\", %p, rules_p, %p): result perm is %p",
424  fpath, ( void * ) ( long ) st_mode, ( void * ) ( long ) ruleactions, ( void * ) ( long ) resultperm );
425  return resultperm;
426 }
427 
rule::num
int num
Definition: ctx.h:207
ctx
Definition: ctx.h:315
MAXRULES
#define MAXRULES
Definition: configuration.h:10
rule::mask
ruleaction_t mask
Definition: ctx.h:211
AUTORULESW
@ AUTORULESW
Definition: ctx.h:75
rule::perm
ruleaction_t perm
Definition: ctx.h:210
rule_complete
int rule_complete(rule_t *rule_p, char *expr, size_t *rules_count_p)
Definition: rules.c:27
RA_ALL
@ RA_ALL
Definition: ctx.h:194
ctx::flags
int flags[(1<< 10)]
Definition: ctx.h:338
ctx::rules_count
size_t rules_count
Definition: ctx.h:334
ctx::rulfpath
char * rulfpath
Definition: ctx.h:385
MODE_RSYNCSO
@ MODE_RSYNCSO
Definition: ctx.h:167
DEFAULT_RULES_PERM
#define DEFAULT_RULES_PERM
Definition: configuration.h:82
MODE_RSYNCSHELL
@ MODE_RSYNCSHELL
Definition: ctx.h:165
RA_WALK
@ RA_WALK
Definition: ctx.h:193
error
#define error(...)
Definition: error.h:36
error.h
RA_MONITOR
@ RA_MONITOR
Definition: ctx.h:191
MODE
@ MODE
Definition: ctx.h:76
debug
#define debug(debug_level,...)
Definition: error.h:50
parse_rules_fromfile
int parse_rules_fromfile(ctx_t *ctx_p)
Definition: rules.c:56
rule::objtype
mode_t objtype
Definition: ctx.h:209
ruleaction_t
enum ruleaction_enum ruleaction_t
Definition: ctx.h:196
rules.h
rules_getperm
ruleaction_t rules_getperm(const char *fpath, mode_t st_mode, rule_t *rules_p, ruleaction_t ruleactions)
Definition: rules.c:403
ctx::rules
rule_t rules[MAXRULES]
Definition: ctx.h:333
RA_NONE
@ RA_NONE
Definition: ctx.h:190
MODE_RSYNCDIRECT
@ MODE_RSYNCDIRECT
Definition: ctx.h:166
warning
#define warning(...)
Definition: error.h:40
BUFSIZ
#define BUFSIZ
Definition: configuration.h:6
common.h
rule
Definition: ctx.h:206
rule::expr
regex_t expr
Definition: ctx.h:208
RS_PERMIT
@ RS_PERMIT
Definition: ctx.h:185
RA_SYNC
@ RA_SYNC
Definition: ctx.h:192
rules_search_getperm
ruleaction_t rules_search_getperm(const char *fpath, mode_t st_mode, rule_t *rules_p, const ruleaction_t ruleaction, rule_t **rule_pp)
Checks file path by rules' expressions (parsed from file)
Definition: rules.c:327
ctx_p
ctx_t * ctx_p
Definition: mon_kqueue.c:85
RS_REJECT
@ RS_REJECT
Definition: ctx.h:184