]> git.ipfire.org Git - thirdparty/bash.git/blob - findcmd.c
asan fuzzing fixes; fix incomplete multibyte chars in history expansion; new nosort...
[thirdparty/bash.git] / findcmd.c
1 /* findcmd.c -- Functions to search for commands by name. */
2
3 /* Copyright (C) 1997-2023 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include "chartypes.h"
25 #include "bashtypes.h"
26 #if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
27 # include <sys/file.h>
28 #endif
29 #include "filecntl.h"
30 #include "posixstat.h"
31
32 #if defined (HAVE_UNISTD_H)
33 # include <unistd.h>
34 #endif
35 #include <errno.h>
36
37 #include "bashansi.h"
38
39 #include "memalloc.h"
40 #include "shell.h"
41 #include "execute_cmd.h"
42 #include "flags.h"
43 #include "hashlib.h"
44 #include "pathexp.h"
45 #include "hashcmd.h"
46 #include "findcmd.h" /* matching prototypes and declarations */
47
48 #include <glob/strmatch.h>
49
50 #if !defined (errno)
51 extern int errno;
52 #endif
53
54 /* Static functions defined and used in this file. */
55 static char *_find_user_command_internal (const char *, int);
56 static char *find_user_command_internal (const char *, int);
57 static char *find_user_command_in_path (const char *, char *, int, int *);
58 static char *find_in_path_element (const char *, char *, int, size_t, struct stat *, int *);
59 static char *find_absolute_program (const char *, int);
60
61 static char *get_next_path_element (char *, int *);
62
63 /* The file name which we would try to execute, except that it isn't
64 possible to execute it. This is the first file that matches the
65 name that we are looking for while we are searching $PATH for a
66 suitable one to execute. If we cannot find a suitable executable
67 file, then we use this one. */
68 static char *file_to_lose_on;
69
70 /* Non-zero if we should stat every command found in the hash table to
71 make sure it still exists. */
72 int check_hashed_filenames = CHECKHASH_DEFAULT;
73
74 /* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
75 encounters a `.' as the directory pathname while scanning the
76 list of possible pathnames; i.e., if `.' comes before the directory
77 containing the file of interest. */
78 int dot_found_in_search = 0;
79
80 /* Set up EXECIGNORE; a blacklist of patterns that executable files should not
81 match. */
82 static struct ignorevar execignore =
83 {
84 "EXECIGNORE",
85 NULL,
86 0,
87 NULL,
88 NULL
89 };
90
91 void
92 setup_exec_ignore (const char *varname)
93 {
94 setup_ignore_patterns (&execignore);
95 }
96
97 static int
98 exec_name_should_ignore (const char *name)
99 {
100 struct ign *p;
101
102 for (p = execignore.ignores; p && p->val; p++)
103 if (strmatch (p->val, (char *)name, FNMATCH_EXTFLAG|FNM_CASEFOLD) != FNM_NOMATCH)
104 return 1;
105 return 0;
106 }
107
108 /* Return some flags based on information about this file.
109 The EXISTS bit is non-zero if the file is found.
110 The EXECABLE bit is non-zero the file is executable.
111 Zero is returned if the file is not found. */
112 int
113 file_status (const char *name)
114 {
115 struct stat finfo;
116 int r;
117
118 /* Determine whether this file exists or not. */
119 if (stat (name, &finfo) < 0)
120 return (0);
121
122 /* If the file is a directory, then it is not "executable" in the
123 sense of the shell. */
124 if (S_ISDIR (finfo.st_mode))
125 return (FS_EXISTS|FS_DIRECTORY);
126
127 r = FS_EXISTS;
128
129 #if defined (HAVE_EACCESS)
130 /* Use eaccess(2) if we have it to take things like ACLs and other
131 file access mechanisms into account. eaccess uses the effective
132 user and group IDs, not the real ones. We could use sh_eaccess,
133 but we don't want any special treatment for /dev/fd. */
134 if (exec_name_should_ignore (name) == 0 && eaccess (name, X_OK) == 0)
135 r |= FS_EXECABLE;
136 if (eaccess (name, R_OK) == 0)
137 r |= FS_READABLE;
138
139 return r;
140 #elif defined (AFS)
141 /* We have to use access(2) to determine access because AFS does not
142 support Unix file system semantics. This may produce wrong
143 answers for non-AFS files when ruid != euid. I hate AFS. */
144 if (exec_name_should_ignore (name) == 0 && access (name, X_OK) == 0)
145 r |= FS_EXECABLE;
146 if (access (name, R_OK) == 0)
147 r |= FS_READABLE;
148
149 return r;
150 #else /* !HAVE_EACCESS && !AFS */
151
152 /* Find out if the file is actually executable. By definition, the
153 only other criteria is that the file has an execute bit set that
154 we can use. The same with whether or not a file is readable. */
155
156 /* Root only requires execute permission for any of owner, group or
157 others to be able to exec a file, and can read any file. */
158 if (current_user.euid == (uid_t)0)
159 {
160 r |= FS_READABLE;
161 if (exec_name_should_ignore (name) == 0 && (finfo.st_mode & S_IXUGO))
162 r |= FS_EXECABLE;
163 return r;
164 }
165
166 /* If we are the owner of the file, the owner bits apply. */
167 if (current_user.euid == finfo.st_uid)
168 {
169 if (exec_name_should_ignore (name) == 0 && (finfo.st_mode & S_IXUSR))
170 r |= FS_EXECABLE;
171 if (finfo.st_mode & S_IRUSR)
172 r |= FS_READABLE;
173 }
174
175 /* If we are in the owning group, the group permissions apply. */
176 else if (group_member (finfo.st_gid))
177 {
178 if (exec_name_should_ignore (name) == 0 && (finfo.st_mode & S_IXGRP))
179 r |= FS_EXECABLE;
180 if (finfo.st_mode & S_IRGRP)
181 r |= FS_READABLE;
182 }
183
184 /* Else we check whether `others' have permission to execute the file */
185 else
186 {
187 if (exec_name_should_ignore (name) == 0 && finfo.st_mode & S_IXOTH)
188 r |= FS_EXECABLE;
189 if (finfo.st_mode & S_IROTH)
190 r |= FS_READABLE;
191 }
192
193 return r;
194 #endif /* !AFS */
195 }
196
197 /* Return non-zero if FILE exists and is executable.
198 Note that this function is the definition of what an
199 executable file is; do not change this unless YOU know
200 what an executable file is. */
201 int
202 executable_file (const char *file)
203 {
204 int s;
205
206 s = file_status (file);
207 #if defined (EISDIR)
208 if (s & FS_DIRECTORY)
209 errno = EISDIR; /* let's see if we can improve error messages */
210 #endif
211 return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
212 }
213
214 int
215 is_directory (const char *file)
216 {
217 return (file_status (file) & FS_DIRECTORY);
218 }
219
220 int
221 executable_or_directory (const char *file)
222 {
223 int s;
224
225 s = file_status (file);
226 return ((s & FS_EXECABLE) || (s & FS_DIRECTORY));
227 }
228
229 /* Locate the executable file referenced by NAME, searching along
230 the contents of the shell PATH variable. Return a new string
231 which is the full pathname to the file, or NULL if the file
232 couldn't be found. If a file is found that isn't executable,
233 and that is the only match, then return that. */
234 char *
235 find_user_command (const char *name)
236 {
237 return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
238 }
239
240 /* Locate the file referenced by NAME, searching along the contents
241 of the shell PATH variable. Return a new string which is the full
242 pathname to the file, or NULL if the file couldn't be found. This
243 returns the first readable file found; designed to be used to look
244 for shell scripts or files to source. */
245 char *
246 find_path_file (const char *name)
247 {
248 return (find_user_command_internal (name, FS_READABLE));
249 }
250
251 /* Get $PATH and normalize it. USE_TEMPENV, if non-zero, says to look in the
252 temporary environment first. Normalizing means converting PATH= into ".". */
253 char *
254 path_value (const char *pathvar, int use_tempenv)
255 {
256 SHELL_VAR *var;
257 char *path;
258
259 var = use_tempenv ? find_variable_tempenv (pathvar) : find_variable (pathvar);
260 path = var ? value_cell (var) : (char *)NULL;
261
262 if (path == 0 || *path)
263 return (path);
264 else /* *path == '\0' */
265 return ".";
266 }
267
268 static char *
269 _find_user_command_internal (const char *name, int flags)
270 {
271 char *path_list, *cmd;
272 SHELL_VAR *var;
273
274 /* Search for the value of PATH in both the temporary environments and
275 in the regular list of variables. */
276 path_list = path_value ("PATH", 1);
277
278 if (path_list == 0)
279 return (savestring (name));
280
281 cmd = find_user_command_in_path (name, path_list, flags, (int *)0);
282
283 return (cmd);
284 }
285
286 static char *
287 find_user_command_internal (const char *name, int flags)
288 {
289 #ifdef __WIN32__
290 char *res, *dotexe;
291
292 dotexe = (char *)xmalloc (strlen (name) + 5);
293 strcpy (dotexe, name);
294 strcat (dotexe, ".exe");
295 res = _find_user_command_internal (dotexe, flags);
296 free (dotexe);
297 if (res == 0)
298 res = _find_user_command_internal (name, flags);
299 return res;
300 #else
301 return (_find_user_command_internal (name, flags));
302 #endif
303 }
304
305 /* Return the next element from PATH_LIST, a colon separated list of
306 paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
307 the index is modified by this function.
308 Return the next element of PATH_LIST or NULL if there are no more. */
309 static char *
310 get_next_path_element (char *path_list, int *path_index_pointer)
311 {
312 char *path;
313
314 path = extract_colon_unit (path_list, path_index_pointer);
315
316 if (path == 0)
317 return (path);
318
319 if (*path == '\0')
320 {
321 free (path);
322 path = savestring (".");
323 }
324
325 return (path);
326 }
327
328 /* Look for PATHNAME in $PATH. Returns either the hashed command
329 corresponding to PATHNAME or the first instance of PATHNAME found
330 in $PATH. If (FLAGS&CMDSRCH_HASH) is non-zero, insert the instance of
331 PATHNAME found in $PATH into the command hash table.
332 If (FLAGS&CMDSRCH_STDPATH) is non-zero, we are running in a `command -p'
333 environment and should use the Posix standard path.
334 Returns a newly-allocated string. */
335 char *
336 search_for_command (const char *pathname, int flags)
337 {
338 char *hashed_file, *command, *path_list;
339 int temp_path, st;
340 SHELL_VAR *path;
341
342 hashed_file = command = (char *)NULL;
343
344 /* If PATH is in the temporary environment for this command, don't use the
345 hash table to search for the full pathname. */
346 path = find_variable_tempenv ("PATH");
347 temp_path = path && tempvar_p (path);
348
349 /* Don't waste time trying to find hashed data for a pathname
350 that is already completely specified or if we're using a command-
351 specific value for PATH. */
352 if (temp_path == 0 && (flags & CMDSRCH_STDPATH) == 0 && absolute_program (pathname) == 0)
353 hashed_file = phash_search (pathname);
354
355 /* If a command found in the hash table no longer exists, we need to
356 look for it in $PATH. Thank you Posix.2. This forces us to stat
357 every command found in the hash table. */
358
359 if (hashed_file && (posixly_correct || check_hashed_filenames))
360 {
361 st = file_status (hashed_file);
362 if ((st & (FS_EXISTS|FS_EXECABLE)) != (FS_EXISTS|FS_EXECABLE))
363 {
364 phash_remove (pathname);
365 free (hashed_file);
366 hashed_file = (char *)NULL;
367 }
368 }
369
370 if (hashed_file)
371 command = hashed_file;
372 else if (absolute_program (pathname))
373 /* A command containing a slash is not looked up in PATH or saved in
374 the hash table. */
375 command = savestring (pathname);
376 else
377 {
378 if (flags & CMDSRCH_STDPATH)
379 path_list = conf_standard_path ();
380 else if (temp_path || path)
381 {
382 path_list = value_cell (path);
383 if (path_list && *path_list == '\0')
384 path_list = ".";
385 }
386 else
387 path_list = 0;
388
389 command = find_user_command_in_path (pathname, path_list, FS_EXEC_PREFERRED|FS_NODIRS, &st);
390
391 if (command && hashing_enabled && temp_path == 0 && (flags & CMDSRCH_HASH))
392 {
393 /* If we found the full pathname the same as the command name, the
394 command probably doesn't exist. Don't put it into the hash
395 table unless it's an executable file in the current directory. */
396 if (STREQ (command, pathname))
397 {
398 if (path_list == 0)
399 {
400 dot_found_in_search = 1;
401 st = file_status (pathname);
402 }
403 if (st & FS_EXECABLE)
404 phash_insert ((char *)pathname, command, dot_found_in_search, 1);
405 }
406 /* If we're in posix mode, don't add files without the execute bit
407 to the hash table. */
408 else if (posixly_correct || check_hashed_filenames)
409 {
410 if (st & FS_EXECABLE)
411 phash_insert ((char *)pathname, command, dot_found_in_search, 1);
412 }
413 else
414 phash_insert ((char *)pathname, command, dot_found_in_search, 1);
415 }
416
417 if (flags & CMDSRCH_STDPATH)
418 free (path_list);
419 }
420
421 return (command);
422 }
423
424 char *
425 user_command_matches (const char *name, int flags, int state)
426 {
427 register int i;
428 int path_index;
429 size_t name_len;
430 char *path_list, *path_element, *match;
431 struct stat dotinfo;
432 static char **match_list = NULL;
433 static size_t match_list_size = 0;
434 static size_t match_index = 0;
435
436 if (state == 0)
437 {
438 /* Create the list of matches. */
439 if (match_list == 0)
440 {
441 match_list_size = 5;
442 match_list = strvec_create (match_list_size);
443 }
444
445 /* Clear out the old match list. */
446 for (i = 0; i < match_list_size; i++)
447 match_list[i] = 0;
448
449 /* We haven't found any files yet. */
450 match_index = 0;
451
452 if (absolute_program (name))
453 {
454 match_list[0] = find_absolute_program (name, flags);
455 match_list[1] = (char *)NULL;
456 path_list = (char *)NULL;
457 }
458 else
459 {
460 name_len = strlen (name);
461 file_to_lose_on = (char *)NULL;
462 dot_found_in_search = 0;
463 if (stat (".", &dotinfo) < 0)
464 dotinfo.st_dev = dotinfo.st_ino = 0; /* so same_file won't match */
465 path_list = path_value ("PATH", 0);
466 path_index = 0;
467 }
468
469 while (path_list && path_list[path_index])
470 {
471 path_element = get_next_path_element (path_list, &path_index);
472
473 if (path_element == 0)
474 break;
475
476 match = find_in_path_element (name, path_element, flags, name_len, &dotinfo, (int *)0);
477 free (path_element);
478
479 if (match == 0)
480 continue;
481
482 if (match_index + 1 == match_list_size)
483 {
484 match_list_size += 10;
485 match_list = strvec_resize (match_list, (match_list_size + 1));
486 }
487
488 match_list[match_index++] = match;
489 match_list[match_index] = (char *)NULL;
490 FREE (file_to_lose_on);
491 file_to_lose_on = (char *)NULL;
492 }
493
494 /* We haven't returned any strings yet. */
495 match_index = 0;
496 }
497
498 match = match_list[match_index];
499
500 if (match)
501 match_index++;
502
503 return (match);
504 }
505
506 static char *
507 find_absolute_program (const char *name, int flags)
508 {
509 int st;
510
511 st = file_status (name);
512
513 /* If the file doesn't exist, quit now. */
514 if ((st & FS_EXISTS) == 0)
515 return ((char *)NULL);
516
517 /* If we only care about whether the file exists or not, return
518 this filename. Otherwise, maybe we care about whether this
519 file is executable. If it is, and that is what we want, return it. */
520 if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
521 return (savestring (name));
522
523 return (NULL);
524 }
525
526 static char *
527 find_in_path_element (const char *name, char *path, int flags, size_t name_len, struct stat *dotinfop, int *rflagsp)
528 {
529 int status;
530 char *full_path, *xpath;
531
532 xpath = (posixly_correct == 0 && *path == '~') ? bash_tilde_expand (path, 0) : path;
533
534 /* Remember the location of "." in the path, in all its forms
535 (as long as they begin with a `.', e.g. `./.') */
536 /* We could also do this or something similar for all relative pathnames
537 found while searching PATH. */
538 if (dot_found_in_search == 0 && *xpath == '.')
539 dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
540
541 full_path = sh_makepath (xpath, name, 0);
542
543 status = file_status (full_path);
544
545 if (xpath != path)
546 free (xpath);
547
548 if (rflagsp)
549 *rflagsp = status;
550
551 if ((status & FS_EXISTS) == 0)
552 {
553 free (full_path);
554 return ((char *)NULL);
555 }
556
557 /* The file exists. If the caller simply wants the first file, here it is. */
558 if (flags & FS_EXISTS)
559 return (full_path);
560
561 /* If we have a readable file, and the caller wants a readable file, this
562 is it. */
563 if ((flags & FS_READABLE) && (status & FS_READABLE))
564 return (full_path);
565
566 /* If the file is executable, then it satisfies the cases of
567 EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
568 if ((status & FS_EXECABLE) && (flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) &&
569 (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
570 {
571 FREE (file_to_lose_on);
572 file_to_lose_on = (char *)NULL;
573 return (full_path);
574 }
575
576 /* The file is not executable, but it does exist. If we prefer
577 an executable, then remember this one if it is the first one
578 we have found. */
579 if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0 && exec_name_should_ignore (full_path) == 0)
580 file_to_lose_on = savestring (full_path);
581
582 /* If we want only executable files, or we don't want directories and
583 this file is a directory, or we want a readable file and this file
584 isn't readable, fail. */
585 if ((flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) ||
586 ((flags & FS_NODIRS) && (status & FS_DIRECTORY)) ||
587 ((flags & FS_READABLE) && (status & FS_READABLE) == 0))
588 {
589 free (full_path);
590 return ((char *)NULL);
591 }
592 else
593 return (full_path);
594 }
595
596 /* This does the dirty work for find_user_command_internal () and
597 user_command_matches ().
598 NAME is the name of the file to search for.
599 PATH_LIST is a colon separated list of directories to search. It is the
600 caller's responsibility to pass a non-empty path if they want an empty
601 path to be treated specially.
602 FLAGS contains bit fields which control the files which are eligible.
603 Some values are:
604 FS_EXEC_ONLY: The file must be an executable to be found.
605 FS_EXEC_PREFERRED: If we can't find an executable, then the
606 the first file matching NAME will do.
607 FS_EXISTS: The first file found will do.
608 FS_NODIRS: Don't find any directories.
609 */
610 static char *
611 find_user_command_in_path (const char *name, char *path_list, int flags, int *rflagsp)
612 {
613 char *full_path, *path;
614 int path_index, rflags;
615 size_t name_len;
616 struct stat dotinfo;
617
618 /* We haven't started looking, so we certainly haven't seen
619 a `.' as the directory path yet. */
620 dot_found_in_search = 0;
621
622 if (rflagsp)
623 *rflagsp = 0;
624
625 if (absolute_program (name))
626 {
627 full_path = find_absolute_program (name, flags);
628 return (full_path);
629 }
630
631 if (path_list == 0 || *path_list == '\0')
632 return (savestring (name)); /* XXX */
633
634 file_to_lose_on = (char *)NULL;
635 name_len = strlen (name);
636 if (stat (".", &dotinfo) < 0)
637 dotinfo.st_dev = dotinfo.st_ino = 0;
638 path_index = 0;
639
640 while (path_list[path_index])
641 {
642 /* Allow the user to interrupt out of a lengthy path search. */
643 QUIT;
644
645 path = get_next_path_element (path_list, &path_index);
646 if (path == 0)
647 break;
648
649 /* Side effects: sets dot_found_in_search, possibly sets
650 file_to_lose_on. */
651 full_path = find_in_path_element (name, path, flags, name_len, &dotinfo, &rflags);
652 free (path);
653
654 /* We use the file status flag bits to check whether full_path is a
655 directory, which we reject here. */
656 if (full_path && (rflags & FS_DIRECTORY))
657 {
658 free (full_path);
659 continue;
660 }
661
662 if (full_path)
663 {
664 if (rflagsp)
665 *rflagsp = rflags;
666 FREE (file_to_lose_on);
667 return (full_path);
668 }
669 }
670
671 /* We didn't find exactly what the user was looking for. Return
672 the contents of FILE_TO_LOSE_ON which is NULL when the search
673 required an executable, or non-NULL if a file was found and the
674 search would accept a non-executable as a last resort. If the
675 caller specified FS_NODIRS, and file_to_lose_on is a directory,
676 return NULL. */
677 if (file_to_lose_on && (flags & FS_NODIRS) && file_isdir (file_to_lose_on))
678 {
679 free (file_to_lose_on);
680 file_to_lose_on = (char *)NULL;
681 }
682
683 return (file_to_lose_on);
684 }
685
686 /* External interface to find a command given a $PATH. Separate from
687 find_user_command_in_path to allow future customization. */
688 char *
689 find_in_path (const char *name, char *path_list, int flags)
690 {
691 return (find_user_command_in_path (name, path_list, flags, (int *)0));
692 }