1 /* Test of execution of file name canonicalization.
2    Copyright (C) 2007-2020 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
18 
19 /* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
20    may "optimize" the null_ptr function, when its result gets passed to a
21    function that has an argument declared as _GL_ARG_NONNULL.  */
22 #define _GL_ARG_NONNULL(params)
23 
24 #include <config.h>
25 
26 #include <stdlib.h>
27 
28 #include "signature.h"
29 SIGNATURE_CHECK (realpath, char *, (const char *, char *));
30 SIGNATURE_CHECK (canonicalize_file_name, char *, (const char *));
31 
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 
39 #include "same-inode.h"
40 #include "ignore-value.h"
41 
42 #if GNULIB_defined_canonicalize_file_name
43 # include "null-ptr.h"
44 #endif
45 
46 #include "macros.h"
47 
48 #define BASE "t-can-lgpl.tmp"
49 
50 int
main(void)51 main (void)
52 {
53 #if GNULIB_TEST_CANONICALIZE
54   /* No need to test canonicalize-lgpl module if canonicalize is also
55      in use.  */
56   return 0;
57 #endif
58 
59   /* Setup some hierarchy to be used by this test.  Start by removing
60      any leftovers from a previous partial run.  */
61   {
62     int fd;
63     ignore_value (system ("rm -rf " BASE " ise"));
64     ASSERT (mkdir (BASE, 0700) == 0);
65     fd = creat (BASE "/tra", 0600);
66     ASSERT (0 <= fd);
67     ASSERT (close (fd) == 0);
68   }
69 
70   /* Check for ., .., intermediate // handling, and for error cases.  */
71   {
72     char *result = canonicalize_file_name (BASE "//./..//" BASE "/tra");
73     ASSERT (result != NULL);
74     ASSERT (strstr (result, "/" BASE "/tra")
75             == result + strlen (result) - strlen ("/" BASE "/tra"));
76     free (result);
77 
78     errno = 0;
79     result = canonicalize_file_name ("");
80     ASSERT (result == NULL);
81     ASSERT (errno == ENOENT);
82 
83     /* This test works only if the canonicalize_file_name implementation
84        comes from gnulib.  If it comes from libc, we have no way to prevent
85        gcc from "optimizing" the null_ptr function in invalid ways.  See
86        <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93156>.  */
87 #if GNULIB_defined_canonicalize_file_name
88     errno = 0;
89     result = canonicalize_file_name (null_ptr ());
90     ASSERT (result == NULL);
91     ASSERT (errno == EINVAL);
92 #endif
93   }
94 
95   /* Check that a non-directory with trailing slash yields NULL.  */
96   {
97     char *result;
98     errno = 0;
99     result = canonicalize_file_name (BASE "/tra/");
100     ASSERT (result == NULL);
101     ASSERT (errno == ENOTDIR);
102   }
103 
104   /* Check that a missing directory yields NULL.  */
105   {
106     char *result;
107     errno = 0;
108     result = canonicalize_file_name (BASE "/zzz/..");
109     ASSERT (result == NULL);
110     ASSERT (errno == ENOENT);
111   }
112 
113   /* From here on out, tests involve symlinks.  */
114   if (symlink (BASE "/ket", "ise") != 0)
115     {
116       ASSERT (remove (BASE "/tra") == 0);
117       ASSERT (rmdir (BASE) == 0);
118       fputs ("skipping test: symlinks not supported on this file system\n",
119              stderr);
120       return 77;
121     }
122   ASSERT (symlink ("bef", BASE "/plo") == 0);
123   ASSERT (symlink ("tra", BASE "/huk") == 0);
124   ASSERT (symlink ("lum", BASE "/bef") == 0);
125   ASSERT (symlink ("wum", BASE "/ouk") == 0);
126   ASSERT (symlink ("../ise", BASE "/ket") == 0);
127   ASSERT (mkdir (BASE "/lum", 0700) == 0);
128   ASSERT (symlink ("//.//../..", BASE "/droot") == 0);
129 
130   /* Check that the symbolic link to a file can be resolved.  */
131   {
132     char *result1 = canonicalize_file_name (BASE "/huk");
133     char *result2 = canonicalize_file_name (BASE "/tra");
134     ASSERT (result1 != NULL);
135     ASSERT (result2 != NULL);
136     ASSERT (strcmp (result1, result2) == 0);
137     ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/tra"),
138                     "/" BASE "/tra") == 0);
139     free (result1);
140     free (result2);
141   }
142 
143   /* Check that the symbolic link to a directory can be resolved.  */
144   {
145     char *result1 = canonicalize_file_name (BASE "/plo");
146     char *result2 = canonicalize_file_name (BASE "/bef");
147     char *result3 = canonicalize_file_name (BASE "/lum");
148     ASSERT (result1 != NULL);
149     ASSERT (result2 != NULL);
150     ASSERT (result3 != NULL);
151     ASSERT (strcmp (result1, result2) == 0);
152     ASSERT (strcmp (result2, result3) == 0);
153     ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/lum"),
154                     "/" BASE "/lum") == 0);
155     free (result1);
156     free (result2);
157     free (result3);
158   }
159 
160   /* Check that a symbolic link to a nonexistent file yields NULL.  */
161   {
162     char *result;
163     errno = 0;
164     result = canonicalize_file_name (BASE "/ouk");
165     ASSERT (result == NULL);
166     ASSERT (errno == ENOENT);
167   }
168 
169   /* Check that a non-directory symlink with trailing slash yields NULL.  */
170   {
171     char *result;
172     errno = 0;
173     result = canonicalize_file_name (BASE "/huk/");
174     ASSERT (result == NULL);
175     ASSERT (errno == ENOTDIR);
176   }
177 
178   /* Check that a missing directory via symlink yields NULL.  */
179   {
180     char *result;
181     errno = 0;
182     result = canonicalize_file_name (BASE "/ouk/..");
183     ASSERT (result == NULL);
184     ASSERT (errno == ENOENT);
185   }
186 
187   /* Check that a loop of symbolic links is detected.  */
188   {
189     char *result;
190     errno = 0;
191     result = canonicalize_file_name ("ise");
192     ASSERT (result == NULL);
193     ASSERT (errno == ELOOP);
194   }
195 
196   /* Check that leading // is honored correctly.  */
197   {
198     struct stat st1;
199     struct stat st2;
200     char *result1 = canonicalize_file_name ("//.");
201     char *result2 = canonicalize_file_name (BASE "/droot");
202     ASSERT (result1);
203     ASSERT (result2);
204     ASSERT (stat ("/", &st1) == 0);
205     ASSERT (stat ("//", &st2) == 0);
206     /* On IBM z/OS, "/" and "//" are distinct, yet they both have
207        st_dev == st_ino == 1.  */
208 #ifndef __MVS__
209     if (SAME_INODE (st1, st2))
210       {
211         ASSERT (strcmp (result1, "/") == 0);
212         ASSERT (strcmp (result2, "/") == 0);
213       }
214     else
215 #endif
216       {
217         ASSERT (strcmp (result1, "//") == 0);
218         ASSERT (strcmp (result2, "//") == 0);
219       }
220     free (result1);
221     free (result2);
222   }
223 
224 
225   /* Cleanup.  */
226   ASSERT (remove (BASE "/droot") == 0);
227   ASSERT (remove (BASE "/plo") == 0);
228   ASSERT (remove (BASE "/huk") == 0);
229   ASSERT (remove (BASE "/bef") == 0);
230   ASSERT (remove (BASE "/ouk") == 0);
231   ASSERT (remove (BASE "/ket") == 0);
232   ASSERT (remove (BASE "/lum") == 0);
233   ASSERT (remove (BASE "/tra") == 0);
234   ASSERT (remove (BASE) == 0);
235   ASSERT (remove ("ise") == 0);
236 
237   return 0;
238 }
239