(root)/
m4-1.4.19/
tests/
test-canonicalize-lgpl.c
       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 <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
      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 // handling (the easy cases, without symlinks).
      71       This // handling is not mandated by POSIX.  However, many applications
      72       expect that canonicalize_file_name "canonicalizes" the file name,
      73       that is, that different results of canonicalize_file_name correspond
      74       to different files (except for hard links).  */
      75    {
      76      char *result0 = canonicalize_file_name ("/etc/passwd");
      77      if (result0 != NULL) /* This file does not exist on native Windows.  */
      78        {
      79          char *result;
      80  
      81          result = canonicalize_file_name ("/etc//passwd");
      82          ASSERT (result != NULL && strcmp (result, result0) == 0);
      83  
      84          result = canonicalize_file_name ("/etc///passwd");
      85          ASSERT (result != NULL && strcmp (result, result0) == 0);
      86  
      87          /* On Windows, the syntax //host/share/filename denotes a file
      88             in a directory named 'share', exported from host 'host'.
      89             See also m4/double-slash-root.m4.  */
      90  #if !(defined _WIN32 || defined __CYGWIN__)
      91          result = canonicalize_file_name ("//etc/passwd");
      92          ASSERT (result != NULL && strcmp (result, result0) == 0);
      93  
      94          result = canonicalize_file_name ("//etc//passwd");
      95          ASSERT (result != NULL && strcmp (result, result0) == 0);
      96  
      97          result = canonicalize_file_name ("//etc///passwd");
      98          ASSERT (result != NULL && strcmp (result, result0) == 0);
      99  #endif
     100  
     101          result = canonicalize_file_name ("///etc/passwd");
     102          ASSERT (result != NULL && strcmp (result, result0) == 0);
     103  
     104          result = canonicalize_file_name ("///etc//passwd");
     105          ASSERT (result != NULL && strcmp (result, result0) == 0);
     106  
     107          result = canonicalize_file_name ("///etc///passwd");
     108          ASSERT (result != NULL && strcmp (result, result0) == 0);
     109        }
     110    }
     111  
     112    /* Check for ., .., intermediate // handling, and for error cases.  */
     113    {
     114      char *result = canonicalize_file_name (BASE "//./..//" BASE "/tra");
     115      ASSERT (result != NULL);
     116      ASSERT (strstr (result, "/" BASE "/tra")
     117              == result + strlen (result) - strlen ("/" BASE "/tra"));
     118      free (result);
     119  
     120      errno = 0;
     121      result = canonicalize_file_name ("");
     122      ASSERT (result == NULL);
     123      ASSERT (errno == ENOENT);
     124  
     125      /* This test works only if the canonicalize_file_name implementation
     126         comes from gnulib.  If it comes from libc, we have no way to prevent
     127         gcc from "optimizing" the null_ptr function in invalid ways.  See
     128         <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93156>.  */
     129  #if GNULIB_defined_canonicalize_file_name
     130      errno = 0;
     131      result = canonicalize_file_name (null_ptr ());
     132      ASSERT (result == NULL);
     133      ASSERT (errno == EINVAL);
     134  #endif
     135    }
     136  
     137    /* Check that a non-directory with trailing slash yields NULL.  */
     138    {
     139      char *result;
     140      errno = 0;
     141      result = canonicalize_file_name (BASE "/tra/");
     142      ASSERT (result == NULL);
     143      ASSERT (errno == ENOTDIR);
     144    }
     145  
     146    /* Check that a missing directory yields NULL.  */
     147    {
     148      char *result;
     149      errno = 0;
     150      result = canonicalize_file_name (BASE "/zzz/..");
     151      ASSERT (result == NULL);
     152      ASSERT (errno == ENOENT);
     153    }
     154  
     155    /* From here on out, tests involve symlinks.  */
     156    if (symlink (BASE "/ket", "ise") != 0)
     157      {
     158        ASSERT (remove (BASE "/tra") == 0);
     159        ASSERT (rmdir (BASE) == 0);
     160        fputs ("skipping test: symlinks not supported on this file system\n",
     161               stderr);
     162        return 77;
     163      }
     164    ASSERT (symlink ("bef", BASE "/plo") == 0);
     165    ASSERT (symlink ("tra", BASE "/huk") == 0);
     166    ASSERT (symlink ("lum", BASE "/bef") == 0);
     167    ASSERT (symlink ("wum", BASE "/ouk") == 0);
     168    ASSERT (symlink ("../ise", BASE "/ket") == 0);
     169    ASSERT (mkdir (BASE "/lum", 0700) == 0);
     170    ASSERT (symlink ("//.//../..", BASE "/droot") == 0);
     171  
     172    /* Check that the symbolic link to a file can be resolved.  */
     173    {
     174      char *result1 = canonicalize_file_name (BASE "/huk");
     175      char *result2 = canonicalize_file_name (BASE "/tra");
     176      ASSERT (result1 != NULL);
     177      ASSERT (result2 != NULL);
     178      ASSERT (strcmp (result1, result2) == 0);
     179      ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/tra"),
     180                      "/" BASE "/tra") == 0);
     181      free (result1);
     182      free (result2);
     183    }
     184  
     185    /* Check that the symbolic link to a directory can be resolved.  */
     186    {
     187      char *result1 = canonicalize_file_name (BASE "/plo");
     188      char *result2 = canonicalize_file_name (BASE "/bef");
     189      char *result3 = canonicalize_file_name (BASE "/lum");
     190      ASSERT (result1 != NULL);
     191      ASSERT (result2 != NULL);
     192      ASSERT (result3 != NULL);
     193      ASSERT (strcmp (result1, result2) == 0);
     194      ASSERT (strcmp (result2, result3) == 0);
     195      ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/lum"),
     196                      "/" BASE "/lum") == 0);
     197      free (result1);
     198      free (result2);
     199      free (result3);
     200    }
     201  
     202    /* Check that a symbolic link to a nonexistent file yields NULL.  */
     203    {
     204      char *result;
     205      errno = 0;
     206      result = canonicalize_file_name (BASE "/ouk");
     207      ASSERT (result == NULL);
     208      ASSERT (errno == ENOENT);
     209    }
     210  
     211    /* Check that a non-directory symlink with trailing slash yields NULL,
     212       and likewise for other troublesome suffixes.  */
     213    {
     214      char const *const file_name[]
     215        = {
     216           BASE "/huk/",
     217           BASE "/huk/.",
     218           BASE "/huk/./",
     219           BASE "/huk/./.",
     220           BASE "/huk/x",
     221           BASE "/huk/..",
     222           BASE "/huk/../",
     223           BASE "/huk/../.",
     224           BASE "/huk/../x",
     225           BASE "/huk/./..",
     226           BASE "/huk/././../x",
     227          };
     228      for (int i = 0; i < sizeof file_name / sizeof *file_name; i++)
     229        {
     230          errno = 0;
     231          ASSERT (!canonicalize_file_name (file_name[i]));
     232          ASSERT (errno == ENOTDIR);
     233        }
     234    }
     235  
     236    /* Check that a missing directory via symlink yields NULL.  */
     237    {
     238      char *result;
     239      errno = 0;
     240      result = canonicalize_file_name (BASE "/ouk/..");
     241      ASSERT (result == NULL);
     242      ASSERT (errno == ENOENT);
     243    }
     244  
     245    /* Check that a loop of symbolic links is detected.  */
     246    {
     247      char *result;
     248      errno = 0;
     249      result = canonicalize_file_name ("ise");
     250      ASSERT (result == NULL);
     251      ASSERT (errno == ELOOP);
     252    }
     253  
     254    /* Check that leading // within symlinks is honored correctly.  */
     255    {
     256      struct stat st1;
     257      struct stat st2;
     258      char *result1 = canonicalize_file_name ("//.");
     259      char *result2 = canonicalize_file_name (BASE "/droot");
     260      ASSERT (result1);
     261      ASSERT (result2);
     262      ASSERT (stat ("/", &st1) == 0);
     263      ASSERT (stat ("//", &st2) == 0);
     264      /* On IBM z/OS, "/" and "//" are distinct, yet they both have
     265         st_dev == st_ino == 1.  */
     266  #ifndef __MVS__
     267      if (SAME_INODE (st1, st2))
     268        {
     269          ASSERT (strcmp (result1, "/") == 0);
     270          ASSERT (strcmp (result2, "/") == 0);
     271        }
     272      else
     273  #endif
     274        {
     275          ASSERT (strcmp (result1, "//") == 0);
     276          ASSERT (strcmp (result2, "//") == 0);
     277        }
     278      free (result1);
     279      free (result2);
     280    }
     281  
     282  
     283    /* Cleanup.  */
     284    ASSERT (remove (BASE "/droot") == 0);
     285    ASSERT (remove (BASE "/plo") == 0);
     286    ASSERT (remove (BASE "/huk") == 0);
     287    ASSERT (remove (BASE "/bef") == 0);
     288    ASSERT (remove (BASE "/ouk") == 0);
     289    ASSERT (remove (BASE "/ket") == 0);
     290    ASSERT (remove (BASE "/lum") == 0);
     291    ASSERT (remove (BASE "/tra") == 0);
     292    ASSERT (remove (BASE) == 0);
     293    ASSERT (remove ("ise") == 0);
     294  
     295    return 0;
     296  }