aboutsummaryrefslogtreecommitdiff
path: root/selinux
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-07-23 14:03:30 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-07-23 14:03:30 +0000
commit397137b8158298cae14efae330abc57ba11630a5 (patch)
tree66a755944637f3229f3f801c999132e8d9386856 /selinux
parent845db2acf3cf7e87c20cee01dc228de6fad593ed (diff)
downloadbusybox-w32-397137b8158298cae14efae330abc57ba11630a5.tar.gz
busybox-w32-397137b8158298cae14efae330abc57ba11630a5.tar.bz2
busybox-w32-397137b8158298cae14efae330abc57ba11630a5.zip
setfiles,restorecon: new SELinux applets by Yuichi Nakamura <ynakam@hitachisoft.jp>
Diffstat (limited to 'selinux')
-rw-r--r--selinux/Config.in26
-rw-r--r--selinux/Kbuild2
-rw-r--r--selinux/setfiles.c702
3 files changed, 730 insertions, 0 deletions
diff --git a/selinux/Config.in b/selinux/Config.in
index 42bca66d6..77f89c798 100644
--- a/selinux/Config.in
+++ b/selinux/Config.in
@@ -49,6 +49,14 @@ config MATCHPATHCON
49 Enable support to get default security context of the 49 Enable support to get default security context of the
50 specified path from the file contexts configuration. 50 specified path from the file contexts configuration.
51 51
52config RESTORECON
53 bool "restorecon"
54 default n
55 depends on SELINUX
56 help
57 Enable support to relabel files. The feature is almost
58 the same as setfiles, but usage is a little different.
59
52config RUNCON 60config RUNCON
53 bool "runcon" 61 bool "runcon"
54 default n 62 default n
@@ -78,5 +86,23 @@ config SETENFORCE
78 help 86 help
79 Enable support to modify the mode SELinux is running in. 87 Enable support to modify the mode SELinux is running in.
80 88
89config SETFILES
90 bool "setfiles"
91 default n
92 depends on SELINUX
93 help
94 Enable support to modify to relabel files.
95 Notice: If you built libselinux with -D_FILE_OFFSET_BITS=64,
96 (It is default in libselinux's Makefile), you _must_ enable
97 CONFIG_LFS.
98
99config FEATURE_SETFILES_CHECK_OPTION
100 bool "Enable check option"
101 default n
102 depends on SETFILES
103 help
104 Support "-c" option (check the validity of the contexts against
105 the specified binary policy) for setfiles. Requires libsepol.
106
81endmenu 107endmenu
82 108
diff --git a/selinux/Kbuild b/selinux/Kbuild
index 09cae4d2d..08f49c867 100644
--- a/selinux/Kbuild
+++ b/selinux/Kbuild
@@ -14,3 +14,5 @@ lib-$(CONFIG_MATCHPATHCON) += matchpathcon.o
14lib-$(CONFIG_RUNCON) += runcon.o 14lib-$(CONFIG_RUNCON) += runcon.o
15lib-$(CONFIG_SELINUXENABLED) += selinuxenabled.o 15lib-$(CONFIG_SELINUXENABLED) += selinuxenabled.o
16lib-$(CONFIG_SETENFORCE) += setenforce.o 16lib-$(CONFIG_SETENFORCE) += setenforce.o
17lib-$(CONFIG_SETFILES) += setfiles.o
18lib-$(CONFIG_RESTORECON) += setfiles.o
diff --git a/selinux/setfiles.c b/selinux/setfiles.c
new file mode 100644
index 000000000..42207fcfb
--- /dev/null
+++ b/selinux/setfiles.c
@@ -0,0 +1,702 @@
1/*
2 setfiles: based on policycoreutils 2.0.19
3 policycoreutils was released under GPL 2.
4 Port to BusyBox by 2007 Yuichi Nakamura <ynakam@hitachisoft.jp>
5*/
6
7#include "libbb.h"
8#if ENABLE_FEATURE_SETFILES_CHECK_OPTION
9#include <sepol/sepol.h>
10#endif
11
12#define MAX_EXCLUDES 50
13
14struct edir {
15 char *directory;
16 size_t size;
17};
18
19struct globals {
20 FILE *outfile;
21 char *policyfile;
22 char *rootpath;
23 int rootpathlen;
24 unsigned count;
25 int excludeCtr;
26 int errors;
27 int verbose; /* getopt32 uses it, has to be int */
28 //smallint force;
29 //smallint progress;
30 //smallint debug;
31 //smallint dry_run;
32 //smallint quiet;
33 //smallint ignore_enoent;
34 //smallint take_log;
35 //smallint warn_no_match;
36 smallint recurse; /* Recursive descent */
37 smallint follow_mounts;
38 /* Behavior flags determined based on setfiles vs. restorecon */
39 smallint expand_realpath; /* Expand paths via realpath */
40 smallint abort_on_error; /* Abort the file tree walk upon an error */
41 int add_assoc; /* Track inode associations for conflict detection */
42 int matchpathcon_flags; /* Flags to matchpathcon */
43 dev_t dev_id; /* Device id where target file exists */
44 int nerr;
45 struct edir excludeArray[MAX_EXCLUDES];
46};
47
48#define G (*(struct globals*)&bb_common_bufsiz1)
49void BUG_setfiles_globals_too_big(void);
50#define INIT_G() do { \
51 if (sizeof(G) > COMMON_BUFSIZE) \
52 BUG_setfiles_globals_too_big(); \
53 /* memset(&G, 0, sizeof(G)); - already is */ \
54} while (0)
55#define outfile (G.outfile )
56#define policyfile (G.policyfile )
57#define rootpath (G.rootpath )
58#define rootpathlen (G.rootpathlen )
59#define count (G.count )
60#define excludeCtr (G.excludeCtr )
61#define errors (G.errors )
62#define verbose (G.verbose )
63//#define force (G.force )
64//#define progress (G.progress )
65//#define debug (G.debug )
66//#define dry_run (G.dry_run )
67//#define quiet (G.quiet )
68//#define ignore_enoent (G.ignore_enoent )
69//#define take_log (G.take_log )
70//#define warn_no_match (G.warn_no_match )
71#define recurse (G.recurse )
72#define follow_mounts (G.follow_mounts )
73#define expand_realpath (G.expand_realpath )
74#define abort_on_error (G.abort_on_error )
75#define add_assoc (G.add_assoc )
76#define matchpathcon_flags (G.matchpathcon_flags)
77#define dev_id (G.dev_id )
78#define nerr (G.nerr )
79#define excludeArray (G.excludeArray )
80
81/* Must match getopt32 string! */
82enum {
83 OPT_d = (1 << 0),
84 OPT_e = (1 << 1),
85 OPT_f = (1 << 2),
86 OPT_i = (1 << 3),
87 OPT_l = (1 << 4),
88 OPT_n = (1 << 5),
89 OPT_p = (1 << 6),
90 OPT_q = (1 << 7),
91 OPT_r = (1 << 8),
92 OPT_s = (1 << 9),
93 OPT_v = (1 << 10),
94 OPT_o = (1 << 11),
95 OPT_F = (1 << 12),
96 OPT_W = (1 << 13),
97 OPT_c = (1 << 14), /* c only for setfiles */
98 OPT_R = (1 << 14), /* R only for restorecon */
99};
100#define FLAG_d_debug (option_mask32 & OPT_d)
101#define FLAG_e (option_mask32 & OPT_e)
102#define FLAG_f (option_mask32 & OPT_f)
103#define FLAG_i_ignore_enoent (option_mask32 & OPT_i)
104#define FLAG_l_take_log (option_mask32 & OPT_l)
105#define FLAG_n_dry_run (option_mask32 & OPT_n)
106#define FLAG_p_progress (option_mask32 & OPT_p)
107#define FLAG_q_quiet (option_mask32 & OPT_q)
108#define FLAG_r (option_mask32 & OPT_r)
109#define FLAG_s (option_mask32 & OPT_s)
110#define FLAG_v (option_mask32 & OPT_v)
111#define FLAG_o (option_mask32 & OPT_o)
112#define FLAG_F_force (option_mask32 & OPT_F)
113#define FLAG_W_warn_no_match (option_mask32 & OPT_W)
114#define FLAG_c (option_mask32 & OPT_c)
115#define FLAG_R (option_mask32 & OPT_R)
116
117
118static void qprintf(const char *fmt, ...)
119{
120 /* quiet, do nothing */
121}
122
123static void inc_err(void)
124{
125 nerr++;
126 if (nerr > 9 && !FLAG_d_debug) {
127 bb_error_msg_and_die("exiting after 10 errors");
128 }
129}
130
131static int add_exclude(const char *directory)
132{
133 struct stat sb;
134 size_t len = 0;
135
136 if (directory == NULL || directory[0] != '/') {
137 bb_error_msg("full path required for exclude: %s", directory);
138 return 1;
139 }
140 if (lstat(directory, &sb)) {
141 bb_error_msg("directory \"%s\" not found, ignoring", directory);
142 return 0;
143 }
144 if ((sb.st_mode & S_IFDIR) == 0) {
145 bb_error_msg("\"%s\" is not a directory: mode %o, ignoring",
146 directory, sb.st_mode);
147 return 0;
148 }
149 if (excludeCtr == MAX_EXCLUDES) {
150 bb_error_msg("maximum excludes %d exceeded", MAX_EXCLUDES);
151 return 1;
152 }
153
154 len = strlen(directory);
155 while (len > 1 && directory[len - 1] == '/') {
156 len--;
157 }
158 excludeArray[excludeCtr].directory = xstrndup(directory, len);
159 excludeArray[excludeCtr++].size = len;
160
161 return 0;
162}
163
164static int exclude(const char *file)
165{
166 int i = 0;
167 for (i = 0; i < excludeCtr; i++) {
168 if (strncmp(file, excludeArray[i].directory,
169 excludeArray[i].size) == 0) {
170 if (file[excludeArray[i].size] == '\0'
171 || file[excludeArray[i].size] == '/') {
172 return 1;
173 }
174 }
175 }
176 return 0;
177}
178
179static int match(const char *name, struct stat *sb, char **con)
180{
181 int ret;
182 char path[PATH_MAX + 1];
183 char *tmp_path = xstrdup(name);
184
185 if (excludeCtr > 0) {
186 if (exclude(name)) {
187 goto err;
188 }
189 }
190 ret = lstat(name, sb);
191 if (ret) {
192 if (FLAG_i_ignore_enoent && errno == ENOENT) {
193 free(tmp_path);
194 return 0;
195 }
196 bb_error_msg("stat(%s)", name);
197 goto err;
198 }
199
200 if (expand_realpath) {
201 if (S_ISLNK(sb->st_mode)) {
202 char *p = NULL;
203 char *file_sep;
204
205 size_t len = 0;
206
207 if (verbose > 1)
208 bb_error_msg("warning! %s refers to a symbolic link, not following last component", name);
209
210 file_sep = strrchr(tmp_path, '/');
211 if (file_sep == tmp_path) {
212 file_sep++;
213 p[0] = '\0';
214 p = path;
215 } else if (file_sep) {
216 *file_sep++ = '\0';
217 p = realpath(tmp_path, path);
218 } else {
219 file_sep = tmp_path;
220 p = realpath("./", path);
221 }
222 if (p)
223 len = strlen(p);
224 if (!p || len + strlen(file_sep) + 2 > PATH_MAX) {
225 bb_perror_msg("realpath(%s) failed", name);
226 goto err;
227 }
228 p += len;
229 /* ensure trailing slash of directory name */
230 if (len == 0 || p[-1] != '/') {
231 *p++ = '/';
232 }
233 strcpy(p, file_sep);
234 name = path;
235 if (excludeCtr > 0 && exclude(name))
236 goto err;
237
238 } else {
239 char *p;
240 p = realpath(name, path);
241 if (!p) {
242 bb_perror_msg("realpath(%s)", name);
243 goto err;
244 }
245 name = p;
246 if (excludeCtr > 0 && exclude(name))
247 goto err;
248 }
249 }
250
251 /* name will be what is matched in the policy */
252 if (NULL != rootpath) {
253 if (0 != strncmp(rootpath, name, rootpathlen)) {
254 bb_error_msg("%s is not located in %s",
255 name, rootpath);
256 goto err;
257 }
258 name += rootpathlen;
259 }
260
261 free(tmp_path);
262 if (rootpath != NULL && name[0] == '\0')
263 /* this is actually the root dir of the alt root */
264 return matchpathcon_index("/", sb->st_mode, con);
265 return matchpathcon_index(name, sb->st_mode, con);
266 err:
267 free(tmp_path);
268 return -1;
269}
270
271/* Compare two contexts to see if their differences are "significant",
272 * or whether the only difference is in the user. */
273static int only_changed_user(const char *a, const char *b)
274{
275 if (FLAG_F_force)
276 return 0;
277 if (!a || !b)
278 return 0;
279 a = strchr(a, ':'); /* Rest of the context after the user */
280 b = strchr(b, ':');
281 if (!a || !b)
282 return 0;
283 return (strcmp(a, b) == 0);
284}
285
286static int restore(const char *file)
287{
288 char *my_file = xstrdup(file);
289 char *my_file_orig = my_file;
290 struct stat my_sb;
291 int i, j, ret;
292 char *context = NULL;
293 char *newcon = NULL;
294 int user_only_changed = 0;
295 size_t len = strlen(my_file);
296 int retval = 0;
297
298 /* Skip the extra slashes at the beginning and end, if present. */
299 if (file[0] == '/' && file[1] == '/')
300 my_file++;
301 if (len > 1 && my_file[len - 1] == '/')
302 my_file[len - 1] = '\0';
303
304 i = match(my_file, &my_sb, &newcon);
305
306 if (i < 0) /* No matching specification. */
307 goto out;
308
309
310 if (FLAG_p_progress) {
311 count++;
312 if (count % 0x400 == 0) { /* every 1024 times */
313 count = (count % (80*0x400));
314 if (count == 0)
315 fputc('\n', stdout);
316 fputc('*', stdout);
317 fflush(stdout);
318 }
319 }
320
321 /*
322 * Try to add an association between this inode and
323 * this specification. If there is already an association
324 * for this inode and it conflicts with this specification,
325 * then use the last matching specification.
326 */
327 if (add_assoc) {
328 j = matchpathcon_filespec_add(my_sb.st_ino, i, my_file);
329 if (j < 0)
330 goto err;
331
332 if (j != i) {
333 /* There was already an association and it took precedence. */
334 goto out;
335 }
336 }
337
338 if (FLAG_d_debug)
339 printf("%s: %s matched by %s\n", applet_name, my_file, newcon);
340
341 /* Get the current context of the file. */
342 ret = lgetfilecon_raw(my_file, &context);
343 if (ret < 0) {
344 if (errno == ENODATA) {
345 context = NULL; /* paranoia */
346 } else {
347 bb_perror_msg("lgetfilecon_raw on %s", my_file);
348 goto err;
349 }
350 user_only_changed = 0;
351 } else
352 user_only_changed = only_changed_user(context, newcon);
353
354 /*
355 * Do not relabel the file if the matching specification is
356 * <<none>> or the file is already labeled according to the
357 * specification.
358 */
359 if ((strcmp(newcon, "<<none>>") == 0)
360 || (context && (strcmp(context, newcon) == 0) && !FLAG_F_force)) {
361 goto out;
362 }
363
364 if (!FLAG_F_force && context && (is_context_customizable(context) > 0)) {
365 if (verbose > 1) {
366 bb_error_msg("skipping %s. %s is customizable_types",
367 my_file, context);
368 }
369 goto out;
370 }
371
372 if (verbose) {
373 /* If we're just doing "-v", trim out any relabels where
374 * the user has changed but the role and type are the
375 * same. For "-vv", emit everything. */
376 if (verbose > 1 || !user_only_changed) {
377 bb_info_msg("%s: reset %s context %s->%s",
378 applet_name, my_file, context ?: "", newcon);
379 }
380 }
381
382 if (FLAG_l_take_log && !user_only_changed) {
383 if (context)
384 bb_info_msg("relabeling %s from %s to %s", my_file, context, newcon);
385 else
386 bb_info_msg("labeling %s to %s", my_file, newcon);
387 }
388
389 if (outfile && !user_only_changed)
390 fprintf(outfile, "%s\n", my_file);
391
392 /*
393 * Do not relabel the file if -n was used.
394 */
395 if (FLAG_n_dry_run || user_only_changed)
396 goto out;
397
398 /*
399 * Relabel the file to the specified context.
400 */
401 ret = lsetfilecon(my_file, newcon);
402 if (ret) {
403 bb_perror_msg("lsetfileconon(%s,%s)", my_file, newcon);
404 goto err;
405 }
406
407 out:
408 freecon(context);
409 freecon(newcon);
410 free(my_file_orig);
411 return retval;
412 err:
413 retval--; /* -1 */
414 goto out;
415}
416
417/*
418 * Apply the last matching specification to a file.
419 * This function is called by recursive_action on each file during
420 * the directory traversal.
421 */
422static int apply_spec(const char *file,
423 struct stat *sb, void *userData, int depth)
424{
425 if (!follow_mounts) {
426 /* setfiles does not process across different mount points */
427 if (sb->st_dev != dev_id) {
428 return SKIP;
429 }
430 }
431 errors |= restore(file);
432 if (abort_on_error && errors)
433 return FALSE;
434 return TRUE;
435}
436
437
438static int canoncon(const char *path, unsigned lineno, char **contextp)
439{
440 static const char err_msg[] = "%s: line %u has invalid context %s";
441
442 char *tmpcon;
443 char *context = *contextp;
444 int invalid = 0;
445
446#if ENABLE_FEATURE_SETFILES_CHECK_OPTION
447 if (policyfile) {
448 if (sepol_check_context(context) >= 0)
449 return 0;
450 /* Exit immediately if we're in checking mode. */
451 bb_error_msg_and_die(err_msg, path, lineno, context);
452 }
453#endif
454
455 if (security_canonicalize_context_raw(context, &tmpcon) < 0) {
456 if (errno != ENOENT) {
457 invalid = 1;
458 inc_err();
459 }
460 } else {
461 free(context);
462 *contextp = tmpcon;
463 }
464
465 if (invalid) {
466 bb_error_msg(err_msg, path, lineno, context);
467 }
468
469 return invalid;
470}
471
472static int process_one(char *name)
473{
474 struct stat sb;
475 int rc;
476
477 rc = lstat(name, &sb);
478 if (rc < 0) {
479 if (FLAG_i_ignore_enoent && errno == ENOENT)
480 return 0;
481 bb_perror_msg("stat(%s)", name);
482 goto err;
483 }
484 dev_id = sb.st_dev;
485
486 if (S_ISDIR(sb.st_mode) && recurse) {
487 if (recursive_action(name,
488 ACTION_RECURSE,
489 apply_spec,
490 apply_spec,
491 NULL, 0) != TRUE) {
492 bb_error_msg("error while labeling %s", name);
493 goto err;
494 }
495 } else {
496 rc = restore(name);
497 if (rc)
498 goto err;
499 }
500
501 out:
502 if (add_assoc) {
503 if (FLAG_q_quiet)
504 set_matchpathcon_printf(&qprintf);
505 matchpathcon_filespec_eval();
506 set_matchpathcon_printf(NULL);
507 matchpathcon_filespec_destroy();
508 }
509
510 return rc;
511
512 err:
513 rc = -1;
514 goto out;
515}
516
517int setfiles_main(int argc, char **argv);
518int setfiles_main(int argc, char **argv)
519{
520 struct stat sb;
521 int rc, i = 0;
522 const char *input_filename = NULL;
523 int use_input_file = 0;
524 char *buf = NULL;
525 size_t buf_len;
526 int flags;
527 llist_t *exclude_dir = NULL;
528 char *out_filename = NULL;
529
530 INIT_G();
531
532 if (applet_name[0] == 's') { /* "setfiles" */
533 /*
534 * setfiles:
535 * Recursive descent,
536 * Does not expand paths via realpath,
537 * Aborts on errors during the file tree walk,
538 * Try to track inode associations for conflict detection,
539 * Does not follow mounts,
540 * Validates all file contexts at init time.
541 */
542 recurse = 1;
543 abort_on_error = 1;
544 add_assoc = 1;
545 /* follow_mounts = 0; - already is */
546 matchpathcon_flags = MATCHPATHCON_VALIDATE | MATCHPATHCON_NOTRANS;
547 } else {
548 /*
549 * restorecon:
550 * No recursive descent unless -r/-R,
551 * Expands paths via realpath,
552 * Do not abort on errors during the file tree walk,
553 * Do not try to track inode associations for conflict detection,
554 * Follows mounts,
555 * Does lazy validation of contexts upon use.
556 */
557 expand_realpath = 1;
558 follow_mounts = 1;
559 matchpathcon_flags = MATCHPATHCON_NOTRANS;
560 /* restorecon only */
561 selinux_or_die();
562 }
563
564 set_matchpathcon_flags(matchpathcon_flags);
565
566 opt_complementary = "e::vv:v--p:p--v";
567 /* Option order must match OPT_x definitions! */
568 if (applet_name[0] == 'r') { /* restorecon */
569 flags = getopt32(argc, argv, "de:f:ilnpqrsvo:FWR",
570 &exclude_dir, &input_filename, &out_filename, &verbose);
571 } else { /* setfiles */
572 flags = getopt32(argc, argv, "de:f:ilnpqr:svo:FW"
573 USE_FEATURE_SETFILES_CHECK_OPTION("c:"),
574 &exclude_dir, &input_filename, &rootpath, &out_filename,
575 USE_FEATURE_SETFILES_CHECK_OPTION(&policyfile,)
576 &verbose);
577 }
578
579#if ENABLE_FEATURE_SETFILES_CHECK_OPTION
580 if ((applet_name[0] == 's') && (flags & OPT_c)) {
581 FILE *policystream;
582
583 policystream = xfopen(policyfile, "r");
584 if (sepol_set_policydb_from_file(policystream) < 0) {
585 bb_error_msg_and_die("sepol_set_policydb_from_file on %s", policyfile);
586 }
587 fclose(policystream);
588
589 /* Only process the specified file_contexts file, not
590 any .homedirs or .local files, and do not perform
591 context translations. */
592 set_matchpathcon_flags(MATCHPATHCON_BASEONLY |
593 MATCHPATHCON_NOTRANS |
594 MATCHPATHCON_VALIDATE);
595 }
596#endif
597
598 //if (flags & OPT_d) {
599 // debug = 1;
600 //}
601 if (flags & OPT_e) {
602 if (exclude_dir == NULL) {
603 bb_show_usage();
604 }
605 while (exclude_dir) {
606 if (add_exclude(llist_pop(&exclude_dir)))
607 exit(1);
608 }
609 }
610 //if (flags & OPT_i) {
611 // ignore_enoent = 1;
612 //}
613 //if (flags & OPT_l) {
614 // take_log = 1;
615 //}
616 //if (flags & OPT_F) {
617 // force = 1;
618 //}
619 //if (flags & OPT_n) {
620 // dry_run = 1;
621 //}
622 if (flags & OPT_o) {
623 outfile = stdout;
624 if (NOT_LONE_CHAR(out_filename, '-')) {
625 outfile = xfopen(out_filename, "w");
626 }
627 }
628 //if (flags & OPT_q) {
629 // quiet = 1;
630 //}
631 if (applet_name[0] == 'r') { /* restorecon */
632 if (flags & (OPT_r | OPT_R))
633 recurse = 1;
634 } else { /* setfiles */
635 if (flags & OPT_r)
636 rootpathlen = strlen(rootpath);
637 }
638 if (flags & OPT_s) {
639 use_input_file = 1;
640 input_filename = "-";
641 add_assoc = 0;
642 }
643 //if (flags & OPT_p) {
644 // progress = 1;
645 //}
646 //if (flags & OPT_W) {
647 // warn_no_match = 1;
648 //}
649
650 if (applet_name[0] == 's') { /* setfiles */
651 /* Use our own invalid context checking function so that
652 we can support either checking against the active policy or
653 checking against a binary policy file. */
654 set_matchpathcon_canoncon(&canoncon);
655 if (argc == 1)
656 bb_show_usage();
657 if (stat(argv[optind], &sb) < 0) {
658 bb_perror_msg_and_die("%s", argv[optind]);
659 }
660 if (!S_ISREG(sb.st_mode)) {
661 bb_error_msg_and_die("spec file %s is not a regular file", argv[optind]);
662 }
663 /* Load the file contexts configuration and check it. */
664 rc = matchpathcon_init(argv[optind]);
665 if (rc < 0) {
666 bb_perror_msg_and_die("%s", argv[optind]);
667 }
668
669 optind++;
670
671 if (nerr)
672 exit(1);
673 }
674
675 if (use_input_file) {
676 ssize_t len;
677 FILE *f = stdin;
678
679 if (NOT_LONE_CHAR(input_filename, '-'))
680 f = xfopen(input_filename, "r");
681 while ((len = getline(&buf, &buf_len, f)) > 0) {
682 buf[len - 1] = '\0';
683 errors |= process_one(buf);
684 }
685 if (ENABLE_FEATURE_CLEAN_UP)
686 fclose_if_not_stdin(f);
687 } else {
688 if (optind >= argc)
689 bb_show_usage();
690 for (i = optind; i < argc; i++) {
691 errors |= process_one(argv[i]);
692 }
693 }
694
695 if (FLAG_W_warn_no_match)
696 matchpathcon_checkmatches(argv[0]);
697
698 if (ENABLE_FEATURE_CLEAN_UP && outfile)
699 fclose(outfile);
700
701 return errors;
702}