root/maint/gnulib/tests/test-canonicalize.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. main

   1 /* Test of execution of file name canonicalization.
   2    Copyright (C) 2007-2021 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 "canonicalize.h"
  27 
  28 #include <errno.h>
  29 #include <fcntl.h>
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <sys/stat.h>
  34 #include <unistd.h>
  35 
  36 #include "same-inode.h"
  37 #include "ignore-value.h"
  38 
  39 #if GNULIB_defined_canonicalize_file_name
  40 # include "null-ptr.h"
  41 #endif
  42 
  43 #include "macros.h"
  44 
  45 #define BASE "t-can.tmp"
  46 
  47 int
  48 main (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  49 {
  50   /* Setup some hierarchy to be used by this test.  Start by removing
  51      any leftovers from a previous partial run.  */
  52   {
  53     int fd;
  54     ignore_value (system ("rm -rf " BASE " ise"));
  55     ASSERT (mkdir (BASE, 0700) == 0);
  56     fd = creat (BASE "/tra", 0600);
  57     ASSERT (0 <= fd);
  58     ASSERT (close (fd) == 0);
  59   }
  60 
  61   /* Check // handling (the easy cases, without symlinks).
  62      This // handling is not mandated by POSIX.  However, many applications
  63      expect that canonicalize_filename_mode "canonicalizes" the file name,
  64      that is, that different results of canonicalize_filename_mode correspond
  65      to different files (except for hard links).  */
  66   {
  67     char *result0 = canonicalize_file_name ("/etc/passwd");
  68     if (result0 != NULL) /* This file does not exist on native Windows.  */
  69       {
  70         char *result;
  71 
  72         result = canonicalize_filename_mode ("/etc/passwd", CAN_MISSING);
  73         ASSERT (result != NULL && strcmp (result, result0) == 0);
  74 
  75         result = canonicalize_filename_mode ("/etc//passwd", CAN_MISSING);
  76         ASSERT (result != NULL && strcmp (result, result0) == 0);
  77 
  78         result = canonicalize_filename_mode ("/etc///passwd", CAN_MISSING);
  79         ASSERT (result != NULL && strcmp (result, result0) == 0);
  80 
  81         /* On Windows, the syntax //host/share/filename denotes a file
  82            in a directory named 'share', exported from host 'host'.
  83            See also m4/double-slash-root.m4.  */
  84 #if !(defined _WIN32 || defined __CYGWIN__)
  85         result = canonicalize_filename_mode ("//etc/passwd", CAN_MISSING);
  86         ASSERT (result != NULL && strcmp (result, result0) == 0);
  87 
  88         result = canonicalize_filename_mode ("//etc//passwd", CAN_MISSING);
  89         ASSERT (result != NULL && strcmp (result, result0) == 0);
  90 
  91         result = canonicalize_filename_mode ("//etc///passwd", CAN_MISSING);
  92         ASSERT (result != NULL && strcmp (result, result0) == 0);
  93 #endif
  94 
  95         result = canonicalize_filename_mode ("///etc/passwd", CAN_MISSING);
  96         ASSERT (result != NULL && strcmp (result, result0) == 0);
  97 
  98         result = canonicalize_filename_mode ("///etc//passwd", CAN_MISSING);
  99         ASSERT (result != NULL && strcmp (result, result0) == 0);
 100 
 101         result = canonicalize_filename_mode ("///etc///passwd", CAN_MISSING);
 102         ASSERT (result != NULL && strcmp (result, result0) == 0);
 103       }
 104   }
 105 
 106   /* Check for ., .., intermediate // handling, and for error cases.  */
 107   {
 108     char *result1 = canonicalize_file_name (BASE "//./..//" BASE "/tra");
 109     char *result2 = canonicalize_filename_mode (BASE "//./..//" BASE "/tra",
 110                                                 CAN_EXISTING);
 111     ASSERT (result1 != NULL);
 112     ASSERT (result2 != NULL);
 113     ASSERT (strcmp (result1, result2) == 0);
 114     ASSERT (strstr (result1, "/" BASE "/tra")
 115             == result1 + strlen (result1) - strlen ("/" BASE "/tra"));
 116     free (result1);
 117     free (result2);
 118 
 119     errno = 0;
 120     result1 = canonicalize_file_name ("");
 121     ASSERT (result1 == NULL);
 122     ASSERT (errno == ENOENT);
 123 
 124     errno = 0;
 125     result2 = canonicalize_filename_mode ("", CAN_EXISTING);
 126     ASSERT (result2 == NULL);
 127     ASSERT (errno == ENOENT);
 128 
 129     /* This test works only if the canonicalize_file_name implementation
 130        comes from gnulib.  If it comes from libc, we have no way to prevent
 131        gcc from "optimizing" the null_ptr function in invalid ways.  See
 132        <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93156>.  */
 133 #if GNULIB_defined_canonicalize_file_name
 134     errno = 0;
 135     result1 = canonicalize_file_name (null_ptr ());
 136     ASSERT (result1 == NULL);
 137     ASSERT (errno == EINVAL);
 138 #endif
 139 
 140     errno = 0;
 141     result2 = canonicalize_filename_mode (NULL, CAN_EXISTING);
 142     ASSERT (result2 == NULL);
 143     ASSERT (errno == EINVAL);
 144 
 145     errno = 0;
 146     result2 = canonicalize_filename_mode (".", CAN_MISSING | CAN_ALL_BUT_LAST);
 147     ASSERT (result2 == NULL);
 148     ASSERT (errno == EINVAL);
 149   }
 150 
 151   /* Check that a non-directory with trailing slash yields NULL.  */
 152   {
 153     char *result1;
 154     char *result2;
 155     errno = 0;
 156     result1 = canonicalize_file_name (BASE "/tra/");
 157     ASSERT (result1 == NULL);
 158     ASSERT (errno == ENOTDIR);
 159     errno = 0;
 160     result2 = canonicalize_filename_mode (BASE "/tra/", CAN_EXISTING);
 161     ASSERT (result2 == NULL);
 162     ASSERT (errno == ENOTDIR);
 163   }
 164 
 165   /* Check that a missing directory yields NULL.  */
 166   {
 167     char *result1;
 168     char *result2;
 169     errno = 0;
 170     result1 = canonicalize_file_name (BASE "/zzz/..");
 171     ASSERT (result1 == NULL);
 172     ASSERT (errno == ENOENT);
 173     errno = 0;
 174     result2 = canonicalize_filename_mode (BASE "/zzz/..", CAN_EXISTING);
 175     ASSERT (result2 == NULL);
 176     ASSERT (errno == ENOENT);
 177   }
 178 
 179   /* From here on out, tests involve symlinks.  */
 180   if (symlink (BASE "/ket", "ise") != 0)
 181     {
 182       ASSERT (remove (BASE "/tra") == 0);
 183       ASSERT (rmdir (BASE) == 0);
 184       fputs ("skipping test: symlinks not supported on this file system\n",
 185              stderr);
 186       return 77;
 187     }
 188   ASSERT (symlink ("bef", BASE "/plo") == 0);
 189   ASSERT (symlink ("tra", BASE "/huk") == 0);
 190   ASSERT (symlink ("lum", BASE "/bef") == 0);
 191   ASSERT (symlink ("wum", BASE "/ouk") == 0);
 192   ASSERT (symlink ("../ise", BASE "/ket") == 0);
 193   ASSERT (mkdir (BASE "/lum", 0700) == 0);
 194   ASSERT (symlink ("s", BASE "/p") == 0);
 195   ASSERT (symlink ("d", BASE "/s") == 0);
 196   ASSERT (mkdir (BASE "/d", 0700) == 0);
 197   ASSERT (close (creat (BASE "/d/2", 0600)) == 0);
 198   ASSERT (symlink ("../s/2", BASE "/d/1") == 0);
 199   ASSERT (symlink ("//.//../..", BASE "/droot") == 0);
 200 
 201   /* Check that symbolic links are not resolved, with CAN_NOLINKS.  */
 202   {
 203     char *result1 = canonicalize_filename_mode (BASE "/huk", CAN_NOLINKS);
 204     ASSERT (result1 != NULL);
 205     ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/huk"),
 206                     "/" BASE "/huk") == 0);
 207     free (result1);
 208   }
 209 
 210   /* Check that the symbolic link to a file can be resolved.  */
 211   {
 212     char *result1 = canonicalize_file_name (BASE "/huk");
 213     char *result2 = canonicalize_file_name (BASE "/tra");
 214     char *result3 = canonicalize_filename_mode (BASE "/huk", CAN_EXISTING);
 215     ASSERT (result1 != NULL);
 216     ASSERT (result2 != NULL);
 217     ASSERT (result3 != NULL);
 218     ASSERT (strcmp (result1, result2) == 0);
 219     ASSERT (strcmp (result2, result3) == 0);
 220     ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/tra"),
 221                     "/" BASE "/tra") == 0);
 222     free (result1);
 223     free (result2);
 224     free (result3);
 225   }
 226 
 227   /* Check that the symbolic link to a directory can be resolved.  */
 228   {
 229     char *result1 = canonicalize_file_name (BASE "/plo");
 230     char *result2 = canonicalize_file_name (BASE "/bef");
 231     char *result3 = canonicalize_file_name (BASE "/lum");
 232     char *result4 = canonicalize_filename_mode (BASE "/plo", CAN_EXISTING);
 233     ASSERT (result1 != NULL);
 234     ASSERT (result2 != NULL);
 235     ASSERT (result3 != NULL);
 236     ASSERT (result4 != NULL);
 237     ASSERT (strcmp (result1, result2) == 0);
 238     ASSERT (strcmp (result2, result3) == 0);
 239     ASSERT (strcmp (result3, result4) == 0);
 240     ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/lum"),
 241                     "/" BASE "/lum") == 0);
 242     free (result1);
 243     free (result2);
 244     free (result3);
 245     free (result4);
 246   }
 247 
 248   /* Check that a symbolic link to a nonexistent file yields NULL.  */
 249   {
 250     char *result1;
 251     char *result2;
 252     errno = 0;
 253     result1 = canonicalize_file_name (BASE "/ouk");
 254     ASSERT (result1 == NULL);
 255     ASSERT (errno == ENOENT);
 256     errno = 0;
 257     result2 = canonicalize_filename_mode (BASE "/ouk", CAN_EXISTING);
 258     ASSERT (result2 == NULL);
 259     ASSERT (errno == ENOENT);
 260   }
 261 
 262   /* Check that a non-directory symlink with trailing slash yields NULL,
 263      and likewise for other troublesome suffixes.  */
 264   {
 265     char const *const file_name[]
 266       = {
 267          BASE "/huk/",
 268          BASE "/huk/.",
 269          BASE "/huk/./",
 270          BASE "/huk/./.",
 271          BASE "/huk/x",
 272          BASE "/huk/..",
 273          BASE "/huk/../",
 274          BASE "/huk/../.",
 275          BASE "/huk/../x",
 276          BASE "/huk/./..",
 277          BASE "/huk/././../x",
 278         };
 279     for (int i = 0; i < sizeof file_name / sizeof *file_name; i++)
 280       {
 281         errno = 0;
 282         ASSERT (!canonicalize_file_name (file_name[i]));
 283         ASSERT (errno == ENOTDIR);
 284         errno = 0;
 285         ASSERT (!canonicalize_filename_mode (file_name[i], CAN_EXISTING));
 286         ASSERT (errno == ENOTDIR);
 287       }
 288   }
 289 
 290   /* Check that a missing directory via symlink yields NULL.  */
 291   {
 292     char *result1;
 293     char *result2;
 294     errno = 0;
 295     result1 = canonicalize_file_name (BASE "/ouk/..");
 296     ASSERT (result1 == NULL);
 297     ASSERT (errno == ENOENT);
 298     errno = 0;
 299     result2 = canonicalize_filename_mode (BASE "/ouk/..", CAN_EXISTING);
 300     ASSERT (result2 == NULL);
 301     ASSERT (errno == ENOENT);
 302   }
 303 
 304   /* Check that a loop of symbolic links is detected.  */
 305   {
 306     char *result1;
 307     char *result2;
 308     errno = 0;
 309     result1 = canonicalize_file_name ("ise");
 310     ASSERT (result1 == NULL);
 311     ASSERT (errno == ELOOP);
 312     errno = 0;
 313     result2 = canonicalize_filename_mode ("ise", CAN_EXISTING);
 314     ASSERT (result2 == NULL);
 315     ASSERT (errno == ELOOP);
 316   }
 317 
 318   /* Check that alternate modes can resolve missing basenames.  */
 319   {
 320     char *result1 = canonicalize_filename_mode (BASE "/zzz", CAN_ALL_BUT_LAST);
 321     char *result2 = canonicalize_filename_mode (BASE "/zzz", CAN_MISSING);
 322     char *result3 = canonicalize_filename_mode (BASE "/zzz/", CAN_ALL_BUT_LAST);
 323     char *result4 = canonicalize_filename_mode (BASE "/zzz/", CAN_MISSING);
 324     ASSERT (result1 != NULL);
 325     ASSERT (result2 != NULL);
 326     ASSERT (result3 != NULL);
 327     ASSERT (result4 != NULL);
 328     ASSERT (strcmp (result1, result2) == 0);
 329     ASSERT (strcmp (result2, result3) == 0);
 330     ASSERT (strcmp (result3, result4) == 0);
 331     ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/zzz"),
 332                     "/" BASE "/zzz") == 0);
 333     free (result1);
 334     free (result2);
 335     free (result3);
 336     free (result4);
 337   }
 338 
 339   /* Check that alternate modes can resolve broken symlink basenames.  */
 340   {
 341     char *result1 = canonicalize_filename_mode (BASE "/ouk", CAN_ALL_BUT_LAST);
 342     char *result2 = canonicalize_filename_mode (BASE "/ouk", CAN_MISSING);
 343     char *result3 = canonicalize_filename_mode (BASE "/ouk/", CAN_ALL_BUT_LAST);
 344     char *result4 = canonicalize_filename_mode (BASE "/ouk/", CAN_MISSING);
 345     ASSERT (result1 != NULL);
 346     ASSERT (result2 != NULL);
 347     ASSERT (result3 != NULL);
 348     ASSERT (result4 != NULL);
 349     ASSERT (strcmp (result1, result2) == 0);
 350     ASSERT (strcmp (result2, result3) == 0);
 351     ASSERT (strcmp (result3, result4) == 0);
 352     ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/wum"),
 353                     "/" BASE "/wum") == 0);
 354     free (result1);
 355     free (result2);
 356     free (result3);
 357     free (result4);
 358   }
 359 
 360   /* Check that alternate modes can handle missing dirnames.  */
 361   {
 362     char *result1 = canonicalize_filename_mode ("t-can.zzz/zzz", CAN_ALL_BUT_LAST);
 363     char *result2 = canonicalize_filename_mode ("t-can.zzz/zzz", CAN_MISSING);
 364     ASSERT (result1 == NULL);
 365     ASSERT (result2 != NULL);
 366     ASSERT (strcmp (result2 + strlen (result2) - 14, "/t-can.zzz/zzz") == 0);
 367     free (result2);
 368   }
 369 
 370   /* Ensure that the following is resolved properly.
 371      Before 2007-09-27, it would mistakenly report a loop.  */
 372   {
 373     char *result1 = canonicalize_filename_mode (BASE, CAN_EXISTING);
 374     char *result2 = canonicalize_filename_mode (BASE "/p/1", CAN_EXISTING);
 375     ASSERT (result1 != NULL);
 376     ASSERT (result2 != NULL);
 377     ASSERT (strcmp (result2 + strlen (result1), "/d/2") == 0);
 378     free (result1);
 379     free (result2);
 380   }
 381 
 382   /* Check that leading // within symlinks is honored correctly.  */
 383   {
 384     struct stat st1;
 385     struct stat st2;
 386     char *result1 = canonicalize_file_name ("//.");
 387     char *result2 = canonicalize_filename_mode ("//.", CAN_EXISTING);
 388     char *result3 = canonicalize_file_name (BASE "/droot");
 389     char *result4 = canonicalize_filename_mode (BASE "/droot", CAN_EXISTING);
 390     ASSERT (result1);
 391     ASSERT (result2);
 392     ASSERT (result3);
 393     ASSERT (result4);
 394     ASSERT (stat ("/", &st1) == 0);
 395     ASSERT (stat ("//", &st2) == 0);
 396     if (SAME_INODE (st1, st2))
 397       {
 398         ASSERT (strcmp (result1, "/") == 0);
 399         ASSERT (strcmp (result2, "/") == 0);
 400         ASSERT (strcmp (result3, "/") == 0);
 401         ASSERT (strcmp (result4, "/") == 0);
 402       }
 403     else
 404       {
 405         ASSERT (strcmp (result1, "//") == 0);
 406         ASSERT (strcmp (result2, "//") == 0);
 407         ASSERT (strcmp (result3, "//") == 0);
 408         ASSERT (strcmp (result4, "//") == 0);
 409       }
 410     free (result1);
 411     free (result2);
 412     free (result3);
 413     free (result4);
 414   }
 415 
 416   /* Cleanup.  */
 417   ASSERT (remove (BASE "/droot") == 0);
 418   ASSERT (remove (BASE "/d/1") == 0);
 419   ASSERT (remove (BASE "/d/2") == 0);
 420   ASSERT (remove (BASE "/d") == 0);
 421   ASSERT (remove (BASE "/s") == 0);
 422   ASSERT (remove (BASE "/p") == 0);
 423   ASSERT (remove (BASE "/plo") == 0);
 424   ASSERT (remove (BASE "/huk") == 0);
 425   ASSERT (remove (BASE "/bef") == 0);
 426   ASSERT (remove (BASE "/ouk") == 0);
 427   ASSERT (remove (BASE "/ket") == 0);
 428   ASSERT (remove (BASE "/lum") == 0);
 429   ASSERT (remove (BASE "/tra") == 0);
 430   ASSERT (remove (BASE) == 0);
 431   ASSERT (remove ("ise") == 0);
 432 
 433   return 0;
 434 }

/* [previous][next][first][last][top][bottom][index][help] */