aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fcopy/main.c16
-rw-r--r--src/firejail/appimage.c2
-rw-r--r--src/firejail/cgroup.c2
-rw-r--r--src/firejail/firejail.h2
-rw-r--r--src/firejail/fs_bin.c55
-rw-r--r--src/firejail/fs_etc.c2
-rw-r--r--src/firejail/fs_home.c4
-rw-r--r--src/firejail/fs_hostname.c2
-rw-r--r--src/firejail/fs_mkdir.c4
-rw-r--r--src/firejail/main.c16
-rw-r--r--src/firejail/netfilter.c2
-rw-r--r--src/firejail/netns.c2
-rw-r--r--src/firejail/output.c2
-rw-r--r--src/firejail/profile.c10
-rw-r--r--src/firejail/util.c19
15 files changed, 106 insertions, 34 deletions
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index e7b4ffa8a..cbb551125 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -41,6 +41,11 @@ static void copy_file(const char *srcname, const char *destname, mode_t mode, ui
41 assert(destname); 41 assert(destname);
42 mode &= 07777; 42 mode &= 07777;
43 43
44 // don't copy the file if it is already there
45 struct stat s;
46 if (stat(destname, &s) == 0)
47 return;
48
44 // open source 49 // open source
45 int src = open(srcname, O_RDONLY); 50 int src = open(srcname, O_RDONLY);
46 if (src < 0) { 51 if (src < 0) {
@@ -113,10 +118,18 @@ void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid,
113 (void) mode; 118 (void) mode;
114 (void) uid; 119 (void) uid;
115 (void) gid; 120 (void) gid;
121
122 // if the link is already there, don't create it
123 struct stat s;
124 if (stat(linkpath, &s) == 0)
125 return;
126
116 char *rp = realpath(target, NULL); 127 char *rp = realpath(target, NULL);
117 if (rp) { 128 if (rp) {
118 if (symlink(rp, linkpath) == -1) 129 if (symlink(rp, linkpath) == -1) {
130 free(rp);
119 goto errout; 131 goto errout;
132 }
120 free(rp); 133 free(rp);
121 } 134 }
122 else 135 else
@@ -129,6 +142,7 @@ errout:
129} 142}
130 143
131 144
145
132static int first = 1; 146static int first = 1;
133static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) { 147static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) {
134 (void) st; 148 (void) st;
diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c
index 0f7ab40ff..2a045f628 100644
--- a/src/firejail/appimage.c
+++ b/src/firejail/appimage.c
@@ -45,7 +45,7 @@ void appimage_set(const char *appimage) {
45 45
46#ifdef LOOP_CTL_GET_FREE 46#ifdef LOOP_CTL_GET_FREE
47 // check appimage file 47 // check appimage file
48 invalid_filename(appimage); 48 invalid_filename(appimage, 0); // no globbing
49 if (access(appimage, R_OK) == -1) { 49 if (access(appimage, R_OK) == -1) {
50 fprintf(stderr, "Error: cannot access AppImage file\n"); 50 fprintf(stderr, "Error: cannot access AppImage file\n");
51 exit(1); 51 exit(1);
diff --git a/src/firejail/cgroup.c b/src/firejail/cgroup.c
index 70f07dd23..8d6496aab 100644
--- a/src/firejail/cgroup.c
+++ b/src/firejail/cgroup.c
@@ -72,7 +72,7 @@ errout:
72void set_cgroup(const char *path) { 72void set_cgroup(const char *path) {
73 EUID_ASSERT(); 73 EUID_ASSERT();
74 74
75 invalid_filename(path); 75 invalid_filename(path, 0); // no globbing
76 76
77 // path starts with /sys/fs/cgroup 77 // path starts with /sys/fs/cgroup
78 if (strncmp(path, "/sys/fs/cgroup", 14) != 0) 78 if (strncmp(path, "/sys/fs/cgroup", 14) != 0)
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index b9eb68fb0..e10a5d346 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -499,7 +499,7 @@ void notify_other(int fd);
499char *expand_home(const char *path, const char* homedir); 499char *expand_home(const char *path, const char* homedir);
500const char *gnu_basename(const char *path); 500const char *gnu_basename(const char *path);
501uid_t pid_get_uid(pid_t pid); 501uid_t pid_get_uid(pid_t pid);
502void invalid_filename(const char *fname); 502void invalid_filename(const char *fname, int globbing);
503uid_t get_group_id(const char *group); 503uid_t get_group_id(const char *group);
504int remove_directory(const char *path); 504int remove_directory(const char *path);
505void flush_stdin(void); 505void flush_stdin(void);
diff --git a/src/firejail/fs_bin.c b/src/firejail/fs_bin.c
index 9aa227caf..6bd7ecd17 100644
--- a/src/firejail/fs_bin.c
+++ b/src/firejail/fs_bin.c
@@ -23,6 +23,7 @@
23#include <sys/types.h> 23#include <sys/types.h>
24#include <sys/wait.h> 24#include <sys/wait.h>
25#include <unistd.h> 25#include <unistd.h>
26#include <glob.h>
26 27
27static char *paths[] = { 28static char *paths[] = {
28 "/usr/local/bin", 29 "/usr/local/bin",
@@ -146,11 +147,13 @@ errexit:
146} 147}
147 148
148static void duplicate(char *fname, FILE *fplist) { 149static void duplicate(char *fname, FILE *fplist) {
150 assert(fname);
151
149 if (*fname == '~' || strstr(fname, "..")) { 152 if (*fname == '~' || strstr(fname, "..")) {
150 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); 153 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname);
151 exit(1); 154 exit(1);
152 } 155 }
153 invalid_filename(fname); 156 invalid_filename(fname, 0); // no globbing
154 157
155 char *full_path; 158 char *full_path;
156 if (*fname == '/') { 159 if (*fname == '/') {
@@ -203,6 +206,52 @@ static void duplicate(char *fname, FILE *fplist) {
203 free(full_path); 206 free(full_path);
204} 207}
205 208
209static void globbing(char *fname, FILE *fplist) {
210 assert(fname);
211
212 // go directly to duplicate() if no globbing char is present - see man 7 glob
213 if (strrchr(fname, '*') == NULL &&
214 strrchr(fname, '[') == NULL &&
215 strrchr(fname, '?') == NULL)
216 return duplicate(fname, fplist);
217
218 // loop through paths[]
219 int i = 0;
220 while (paths[i]) {
221 // private-bin-no-local can be disabled in /etc/firejail/firejail.config
222 if (checkcfg(CFG_PRIVATE_BIN_NO_LOCAL) && strstr(paths[i], "local/")) {
223 i++;
224 continue;
225 }
226
227 // check file
228 char *pattern;
229 if (asprintf(&pattern, "%s/%s", paths[i], fname) == -1)
230 errExit("asprintf");
231
232 // globbing
233 glob_t globbuf;
234 int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf);
235 if (globerr) {
236 fprintf(stderr, "Error: failed to glob private-bin pattern %s\n", pattern);
237 exit(1);
238 }
239
240 size_t j;
241 for (j = 0; j < globbuf.gl_pathc; j++) {
242 assert(globbuf.gl_pathv[j]);
243 // testing for GLOB_NOCHECK - no pattern matched returns the original pattern
244 if (strcmp(globbuf.gl_pathv[j], pattern) == 0)
245 continue;
246
247 duplicate(globbuf.gl_pathv[j], fplist);
248 }
249
250 globfree(&globbuf);
251 free(pattern);
252 i++;
253 }
254}
206 255
207void fs_private_bin_list(void) { 256void fs_private_bin_list(void) {
208 char *private_list = cfg.bin_private_keep; 257 char *private_list = cfg.bin_private_keep;
@@ -228,9 +277,9 @@ void fs_private_bin_list(void) {
228 } 277 }
229 278
230 char *ptr = strtok(dlist, ","); 279 char *ptr = strtok(dlist, ",");
231 duplicate(ptr, fplist); 280 globbing(ptr, fplist);
232 while ((ptr = strtok(NULL, ",")) != NULL) 281 while ((ptr = strtok(NULL, ",")) != NULL)
233 duplicate(ptr, fplist); 282 globbing(ptr, fplist);
234 free(dlist); 283 free(dlist);
235 fs_logger_print(); 284 fs_logger_print();
236 if (fplist) 285 if (fplist)
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c
index b0835d50b..9502844b2 100644
--- a/src/firejail/fs_etc.c
+++ b/src/firejail/fs_etc.c
@@ -103,7 +103,7 @@ static void duplicate(const char *fname, const char *private_dir, const char *pr
103 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); 103 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname);
104 exit(1); 104 exit(1);
105 } 105 }
106 invalid_filename(fname); 106 invalid_filename(fname, 0); // no globbing
107 107
108 char *src; 108 char *src;
109 if (asprintf(&src, "%s/%s", private_dir, fname) == -1) 109 if (asprintf(&src, "%s/%s", private_dir, fname) == -1)
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 9e3678c33..0de003e58 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -325,7 +325,7 @@ void fs_private(void) {
325// check new private home directory (--private= option) - exit if it fails 325// check new private home directory (--private= option) - exit if it fails
326void fs_check_private_dir(void) { 326void fs_check_private_dir(void) {
327 EUID_ASSERT(); 327 EUID_ASSERT();
328 invalid_filename(cfg.home_private); 328 invalid_filename(cfg.home_private, 0); // no globbing
329 329
330 // Expand the home directory 330 // Expand the home directory
331 char *tmp = expand_home(cfg.home_private, cfg.homedir); 331 char *tmp = expand_home(cfg.home_private, cfg.homedir);
@@ -367,7 +367,7 @@ static char *check_dir_or_file(const char *name) {
367 assert(name); 367 assert(name);
368 368
369 // basic checks 369 // basic checks
370 invalid_filename(name); 370 invalid_filename(name, 0); // no globbing
371 if (arg_debug) 371 if (arg_debug)
372 printf("Private home: checking %s\n", name); 372 printf("Private home: checking %s\n", name);
373 373
diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c
index 42255070c..0cf715fe2 100644
--- a/src/firejail/fs_hostname.c
+++ b/src/firejail/fs_hostname.c
@@ -129,7 +129,7 @@ void fs_resolvconf(void) {
129 129
130char *fs_check_hosts_file(const char *fname) { 130char *fs_check_hosts_file(const char *fname) {
131 assert(fname); 131 assert(fname);
132 invalid_filename(fname); 132 invalid_filename(fname, 0); // no globbing
133 char *rv = expand_home(fname, cfg.homedir); 133 char *rv = expand_home(fname, cfg.homedir);
134 134
135 // no a link 135 // no a link
diff --git a/src/firejail/fs_mkdir.c b/src/firejail/fs_mkdir.c
index 20ffe825a..7975ae323 100644
--- a/src/firejail/fs_mkdir.c
+++ b/src/firejail/fs_mkdir.c
@@ -59,7 +59,7 @@ void fs_mkdir(const char *name) {
59 EUID_ASSERT(); 59 EUID_ASSERT();
60 60
61 // check directory name 61 // check directory name
62 invalid_filename(name); 62 invalid_filename(name, 0); // no globbing
63 char *expanded = expand_home(name, cfg.homedir); 63 char *expanded = expand_home(name, cfg.homedir);
64 if (strncmp(expanded, cfg.homedir, strlen(cfg.homedir)) != 0 && 64 if (strncmp(expanded, cfg.homedir, strlen(cfg.homedir)) != 0 &&
65 strncmp(expanded, "/tmp", 4) != 0) { 65 strncmp(expanded, "/tmp", 4) != 0) {
@@ -99,7 +99,7 @@ void fs_mkfile(const char *name) {
99 EUID_ASSERT(); 99 EUID_ASSERT();
100 100
101 // check file name 101 // check file name
102 invalid_filename(name); 102 invalid_filename(name, 0); // no globbing
103 char *expanded = expand_home(name, cfg.homedir); 103 char *expanded = expand_home(name, cfg.homedir);
104 if (strncmp(expanded, cfg.homedir, strlen(cfg.homedir)) != 0 && 104 if (strncmp(expanded, cfg.homedir, strlen(cfg.homedir)) != 0 &&
105 strncmp(expanded, "/tmp", 4) != 0) { 105 strncmp(expanded, "/tmp", 4) != 0) {
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 54cbf1526..458bba6f6 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -572,7 +572,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
572 exit(1); 572 exit(1);
573 } 573 }
574 char *path = argv[i + 1]; 574 char *path = argv[i + 1];
575 invalid_filename(path); 575 invalid_filename(path, 0); // no globbing
576 if (strstr(path, "..")) { 576 if (strstr(path, "..")) {
577 fprintf(stderr, "Error: invalid file name %s\n", path); 577 fprintf(stderr, "Error: invalid file name %s\n", path);
578 exit(1); 578 exit(1);
@@ -596,13 +596,13 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
596 exit(1); 596 exit(1);
597 } 597 }
598 char *path1 = argv[i + 1]; 598 char *path1 = argv[i + 1];
599 invalid_filename(path1); 599 invalid_filename(path1, 0); // no globbing
600 if (strstr(path1, "..")) { 600 if (strstr(path1, "..")) {
601 fprintf(stderr, "Error: invalid file name %s\n", path1); 601 fprintf(stderr, "Error: invalid file name %s\n", path1);
602 exit(1); 602 exit(1);
603 } 603 }
604 char *path2 = argv[i + 2]; 604 char *path2 = argv[i + 2];
605 invalid_filename(path2); 605 invalid_filename(path2, 0); // no globbing
606 if (strstr(path2, "..")) { 606 if (strstr(path2, "..")) {
607 fprintf(stderr, "Error: invalid file name %s\n", path2); 607 fprintf(stderr, "Error: invalid file name %s\n", path2);
608 exit(1); 608 exit(1);
@@ -626,7 +626,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
626 exit(1); 626 exit(1);
627 } 627 }
628 char *path = argv[i + 1]; 628 char *path = argv[i + 1];
629 invalid_filename(path); 629 invalid_filename(path, 0); // no globbing
630 if (strstr(path, "..")) { 630 if (strstr(path, "..")) {
631 fprintf(stderr, "Error: invalid file name %s\n", path); 631 fprintf(stderr, "Error: invalid file name %s\n", path);
632 exit(1); 632 exit(1);
@@ -1434,7 +1434,7 @@ int main(int argc, char **argv) {
1434 } 1434 }
1435 1435
1436 // check name 1436 // check name
1437 invalid_filename(subdirname); 1437 invalid_filename(subdirname, 0); // no globbing
1438 if (strstr(subdirname, "..") || strstr(subdirname, "/")) { 1438 if (strstr(subdirname, "..") || strstr(subdirname, "/")) {
1439 fprintf(stderr, "Error: invalid overlay name\n"); 1439 fprintf(stderr, "Error: invalid overlay name\n");
1440 exit(1); 1440 exit(1);
@@ -1483,7 +1483,7 @@ int main(int argc, char **argv) {
1483 exit(1); 1483 exit(1);
1484 } 1484 }
1485 custom_profile_dir = expand_home(argv[i] + 15, cfg.homedir); 1485 custom_profile_dir = expand_home(argv[i] + 15, cfg.homedir);
1486 invalid_filename(custom_profile_dir); 1486 invalid_filename(custom_profile_dir, 0); // no globbing
1487 if (!is_dir(custom_profile_dir) || is_link(custom_profile_dir) || strstr(custom_profile_dir, "..")) { 1487 if (!is_dir(custom_profile_dir) || is_link(custom_profile_dir) || strstr(custom_profile_dir, "..")) {
1488 fprintf(stderr, "Error: invalid profile path\n"); 1488 fprintf(stderr, "Error: invalid profile path\n");
1489 exit(1); 1489 exit(1);
@@ -1542,7 +1542,7 @@ int main(int argc, char **argv) {
1542 } 1542 }
1543 1543
1544 1544
1545 invalid_filename(argv[i] + 9); 1545 invalid_filename(argv[i] + 9, 0); // no globbing
1546 1546
1547 // extract chroot dirname 1547 // extract chroot dirname
1548 cfg.chrootdir = argv[i] + 9; 1548 cfg.chrootdir = argv[i] + 9;
@@ -2193,7 +2193,7 @@ int main(int argc, char **argv) {
2193 fprintf(stderr, "Error: --shell=none was already specified.\n"); 2193 fprintf(stderr, "Error: --shell=none was already specified.\n");
2194 return 1; 2194 return 1;
2195 } 2195 }
2196 invalid_filename(argv[i] + 8); 2196 invalid_filename(argv[i] + 8, 0); // no globbing
2197 2197
2198 if (cfg.shell) { 2198 if (cfg.shell) {
2199 fprintf(stderr, "Error: only one user shell can be specified\n"); 2199 fprintf(stderr, "Error: only one user shell can be specified\n");
diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c
index 14b3b54a6..cb0d9d7af 100644
--- a/src/firejail/netfilter.c
+++ b/src/firejail/netfilter.c
@@ -45,7 +45,7 @@ static char *client_filter =
45 45
46void check_netfilter_file(const char *fname) { 46void check_netfilter_file(const char *fname) {
47 EUID_ASSERT(); 47 EUID_ASSERT();
48 invalid_filename(fname); 48 invalid_filename(fname, 0); // no globbing
49 49
50 if (is_dir(fname) || is_link(fname) || strstr(fname, "..") || access(fname, R_OK )) { 50 if (is_dir(fname) || is_link(fname) || strstr(fname, "..") || access(fname, R_OK )) {
51 fprintf(stderr, "Error: invalid network filter file %s\n", fname); 51 fprintf(stderr, "Error: invalid network filter file %s\n", fname);
diff --git a/src/firejail/netns.c b/src/firejail/netns.c
index fdd108652..f52613490 100644
--- a/src/firejail/netns.c
+++ b/src/firejail/netns.c
@@ -49,7 +49,7 @@ void check_netns(const char *nsname) {
49 fprintf(stderr, "Error: invalid netns name %s\n", nsname); 49 fprintf(stderr, "Error: invalid netns name %s\n", nsname);
50 exit(1); 50 exit(1);
51 } 51 }
52 invalid_filename(nsname); 52 invalid_filename(nsname, 0); // no globbing
53 char *control_file = netns_control_file(nsname); 53 char *control_file = netns_control_file(nsname);
54 54
55 EUID_ASSERT(); 55 EUID_ASSERT();
diff --git a/src/firejail/output.c b/src/firejail/output.c
index b99604ec4..4f68b8ca3 100644
--- a/src/firejail/output.c
+++ b/src/firejail/output.c
@@ -48,7 +48,7 @@ void check_output(int argc, char **argv) {
48 drop_privs(0); 48 drop_privs(0);
49 char *outfile = argv[outindex]; 49 char *outfile = argv[outindex];
50 outfile += (enable_stderr)? 16:9; 50 outfile += (enable_stderr)? 16:9;
51 invalid_filename(outfile); 51 invalid_filename(outfile, 0); // no globbing
52 52
53 // do not accept directories, links, and files with ".." 53 // do not accept directories, links, and files with ".."
54 if (strstr(outfile, "..") || is_link(outfile) || is_dir(outfile)) { 54 if (strstr(outfile, "..") || is_link(outfile) || is_dir(outfile)) {
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 789a8b060..a1c94579c 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -925,7 +925,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
925 } 925 }
926 926
927 // check name 927 // check name
928 invalid_filename(subdirname); 928 invalid_filename(subdirname, 0); // no globbing
929 if (strstr(subdirname, "..") || strstr(subdirname, "/")) { 929 if (strstr(subdirname, "..") || strstr(subdirname, "/")) {
930 fprintf(stderr, "Error: invalid overlay name\n"); 930 fprintf(stderr, "Error: invalid overlay name\n");
931 exit(1); 931 exit(1);
@@ -993,8 +993,8 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
993 } 993 }
994 994
995 // check directories 995 // check directories
996 invalid_filename(dname1); 996 invalid_filename(dname1, 0); // no globbing
997 invalid_filename(dname2); 997 invalid_filename(dname2, 0); // no globbing
998 if (strstr(dname1, "..") || strstr(dname2, "..")) { 998 if (strstr(dname1, "..") || strstr(dname2, "..")) {
999 fprintf(stderr, "Error: invalid file name.\n"); 999 fprintf(stderr, "Error: invalid file name.\n");
1000 exit(1); 1000 exit(1);
@@ -1123,7 +1123,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1123 } 1123 }
1124 1124
1125 // some characters just don't belong in filenames 1125 // some characters just don't belong in filenames
1126 invalid_filename(ptr); 1126 invalid_filename(ptr, 1); // globbing
1127 if (strstr(ptr, "..")) { 1127 if (strstr(ptr, "..")) {
1128 if (lineno == 0) 1128 if (lineno == 0)
1129 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr); 1129 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
@@ -1170,7 +1170,7 @@ void profile_read(const char *fname) {
1170 } 1170 }
1171 1171
1172 // check file 1172 // check file
1173 invalid_filename(fname); 1173 invalid_filename(fname, 0); // no globbing
1174 if (strlen(fname) == 0 || is_dir(fname)) { 1174 if (strlen(fname) == 0 || is_dir(fname)) {
1175 fprintf(stderr, "Error: invalid profile file\n"); 1175 fprintf(stderr, "Error: invalid profile file\n");
1176 exit(1); 1176 exit(1);
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 4d1c94c25..559f7ee48 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -747,7 +747,7 @@ uid_t pid_get_uid(pid_t pid) {
747} 747}
748 748
749 749
750void invalid_filename(const char *fname) { 750void invalid_filename(const char *fname, int globbing) {
751// EUID_ASSERT(); 751// EUID_ASSERT();
752 assert(fname); 752 assert(fname);
753 const char *ptr = fname; 753 const char *ptr = fname;
@@ -763,10 +763,19 @@ void invalid_filename(const char *fname) {
763 return; 763 return;
764 764
765 int len = strlen(ptr); 765 int len = strlen(ptr);
766 // file globbing ('*') is allowed 766
767 if (strcspn(ptr, "\\&!?\"'<>%^(){}[];,") != (size_t)len) { 767 if (globbing) {
768 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr); 768 // file globbing ('*?[]') is allowed
769 exit(1); 769 if (strcspn(ptr, "\\&!\"'<>%^(){};,") != (size_t)len) {
770 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
771 exit(1);
772 }
773 }
774 else {
775 if (strcspn(ptr, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) {
776 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
777 exit(1);
778 }
770 } 779 }
771} 780}
772 781