1 /* Filename lookup using a search path
2    Copyright (C) 1995-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <string.h>
20 #include <hurd.h>
21 #include <hurd/lookup.h>
22 
23 /* If FILE_NAME contains a '/', or PATH is NULL, call FUN with FILE_NAME, and
24    return the result (if PREFIXED_NAME is non-NULL, setting *PREFIXED_NAME to
25    NULL).  Otherwise, call FUN repeatedly with FILE_NAME prefixed with each
26    successive `:' separated element of PATH, returning whenever FUN returns
27    0 (if PREFIXED_NAME is non-NULL, setting *PREFIXED_NAME to the resulting
28    prefixed path).  If FUN never returns 0, return the first non-ENOENT
29    return value, or ENOENT if there is none.  */
30 error_t
file_name_path_scan(const char * file_name,const char * path,error_t (* fun)(const char * name),char ** prefixed_name)31 file_name_path_scan (const char *file_name, const char *path,
32 		     error_t (*fun)(const char *name),
33 		     char **prefixed_name)
34 {
35   if (path == NULL || strchr (file_name, '/'))
36     {
37       if (prefixed_name)
38 	*prefixed_name = 0;
39       return (*fun)(file_name);
40     }
41   else
42     {
43       error_t real_err = 0;
44       size_t file_name_len = strlen (file_name);
45 
46       for (;;)
47 	{
48 	  error_t err;
49 	  const char *next = strchr (path, ':') ?: path + strlen (path);
50 	  size_t pfx_len = next - path;
51 	  char pfxed_name[pfx_len + 2 + file_name_len + 1];
52 
53 	  if (pfx_len == 0)
54 	    pfxed_name[pfx_len++] = '.';
55 	  else
56 	    memcpy (pfxed_name, path, pfx_len);
57 	  if (pfxed_name[pfx_len - 1] != '/')
58 	    pfxed_name[pfx_len++] = '/';
59 	  memcpy (pfxed_name + pfx_len, file_name, file_name_len + 1);
60 
61 	  err = (*fun)(pfxed_name);
62 	  if (err == 0)
63 	    {
64 	      if (prefixed_name)
65 		*prefixed_name = __strdup (pfxed_name);
66 	      return 0;
67 	    }
68 	  if (!real_err && err != ENOENT)
69 	    real_err = err;
70 
71 	  if (*next == '\0')
72 	    return real_err ?: ENOENT;
73 	  else
74 	    path = next + 1;
75 	}
76     }
77 }
78 
79 /* Lookup FILE_NAME and return the node opened with FLAGS & MODE in result
80    (see hurd_file_name_lookup for details), but a simple filename (without
81    any directory prefixes) will be consecutively prefixed with the pathnames
82    in the `:' separated list PATH until one succeeds in a successful lookup.
83    If none succeed, then the first error that wasn't ENOENT is returned, or
84    ENOENT if no other errors were returned.  If PREFIXED_NAME is non-NULL,
85    then if RESULT is looked up directly, *PREFIXED_NAME is set to NULL, and
86    if it is looked up using a prefix from PATH, *PREFIXED_NAME is set to
87    malloced storage containing the prefixed name.  */
88 error_t
__hurd_file_name_path_lookup(error_t (* use_init_port)(int which,error_t (* operate)(mach_port_t)),file_t (* get_dtable_port)(int fd),error_t (* lookup)(file_t dir,const char * name,int flags,mode_t mode,retry_type * do_retry,string_t retry_name,mach_port_t * result),const char * file_name,const char * path,int flags,mode_t mode,file_t * result,char ** prefixed_name)89 __hurd_file_name_path_lookup (error_t (*use_init_port)
90 			        (int which, error_t (*operate) (mach_port_t)),
91 			      file_t (*get_dtable_port) (int fd),
92 			      error_t (*lookup)
93 			        (file_t dir, const char *name, int flags, mode_t mode,
94 			         retry_type *do_retry, string_t retry_name,
95 			         mach_port_t *result),
96 			      const char *file_name, const char *path,
97 			      int flags, mode_t mode,
98 			      file_t *result, char **prefixed_name)
99 {
100   error_t scan_lookup (const char *name)
101     {
102       return
103 	__hurd_file_name_lookup (use_init_port, get_dtable_port, lookup,
104 				 name, flags, mode, result);
105     }
106   return file_name_path_scan (file_name, path, scan_lookup, prefixed_name);
107 }
strong_alias(__hurd_file_name_path_lookup,hurd_file_name_path_lookup)108 strong_alias (__hurd_file_name_path_lookup, hurd_file_name_path_lookup)
109 
110 file_t
111 file_name_path_lookup (const char *file_name, const char *path,
112 		       int flags, mode_t mode, char **prefixed_name)
113 {
114   error_t err;
115   file_t result;
116 
117   err = __hurd_file_name_path_lookup (&_hurd_ports_use, &__getdport, 0,
118 				      file_name, path, flags, mode,
119 				      &result, prefixed_name);
120 
121   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
122 }
123