rtld: properly handle root directory in load path (bug 30435)
[platform/upstream/glibc.git] / elf / sln.c
1 /* `sln' program to create symbolic links between files.
2    Copyright (C) 1998-2023 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 <error.h>
20 #include <errno.h>
21 #include <libintl.h>
22 #include <locale.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <limits.h>
31
32 #include "../version.h"
33
34 #define PACKAGE _libc_intl_domainname
35
36 static int makesymlink (const char *src, const char *dest);
37 static int makesymlinks (const char *file);
38 static void usage (void);
39
40 int
41 main (int argc, char **argv)
42 {
43   /* Set locale via LC_ALL.  */
44   setlocale (LC_ALL, "");
45
46   /* Set the text message domain.  */
47   textdomain (PACKAGE);
48
49   switch (argc)
50     {
51     case 2:
52       if (strcmp (argv[1], "--version") == 0) {
53         printf ("sln %s%s\n", PKGVERSION, VERSION);
54         return 0;
55       } else if (strcmp (argv[1], "--help") == 0) {
56         usage ();
57         return 0;
58       }
59       return makesymlinks (argv [1]);
60       break;
61
62     case 3:
63       return makesymlink (argv [1], argv [2]);
64       break;
65
66     default:
67       usage ();
68       return 1;
69       break;
70     }
71 }
72
73 static void
74 usage (void)
75 {
76   printf (_("Usage: sln src dest|file\n\n"));
77   printf (_("For bug reporting instructions, please see:\n\
78 %s.\n"), REPORT_BUGS_TO);
79 }
80
81 static int
82 makesymlinks (const char *file)
83 {
84   char *buffer = NULL;
85   size_t bufferlen = 0;
86   int ret;
87   int lineno;
88   FILE *fp;
89
90   if (strcmp (file, "-") == 0)
91     fp = stdin;
92   else
93     {
94       fp = fopen (file, "r");
95       if (fp == NULL)
96         {
97           fprintf (stderr, _("%s: file open error: %m\n"), file);
98           return 1;
99         }
100     }
101
102   ret = 0;
103   lineno = 0;
104   while (!feof_unlocked (fp))
105     {
106       ssize_t n = getline (&buffer, &bufferlen, fp);
107       char *src;
108       char *dest;
109       char *cp = buffer;
110
111       if (n < 0)
112         break;
113       if (buffer[n - 1] == '\n')
114         buffer[n - 1] = '\0';
115
116       ++lineno;
117       while (isspace (*cp))
118         ++cp;
119       if (*cp == '\0')
120         /* Ignore empty lines.  */
121         continue;
122       src = cp;
123
124       do
125         ++cp;
126       while (*cp != '\0' && ! isspace (*cp));
127       if (*cp != '\0')
128         *cp++ = '\0';
129
130       while (isspace (*cp))
131         ++cp;
132       if (*cp == '\0')
133         {
134           fprintf (stderr, _("No target in line %d\n"), lineno);
135           ret = 1;
136           continue;
137         }
138       dest = cp;
139
140       do
141         ++cp;
142       while (*cp != '\0' && ! isspace (*cp));
143       if (*cp != '\0')
144         *cp++ = '\0';
145
146       ret |= makesymlink (src, dest);
147     }
148   fclose (fp);
149
150   return ret;
151 }
152
153 static int
154 makesymlink (const char *src, const char *dest)
155 {
156   struct stat stats;
157   const char *error;
158
159   /* Destination must not be a directory. */
160   if (lstat (dest, &stats) == 0)
161     {
162       if (S_ISDIR (stats.st_mode))
163         {
164           fprintf (stderr, _("%s: destination must not be a directory\n"),
165                    dest);
166           return 1;
167         }
168       else if (unlink (dest) && errno != ENOENT)
169         {
170           fprintf (stderr, _("%s: failed to remove the old destination\n"),
171                    dest);
172           return 1;
173         }
174     }
175   else if (errno != ENOENT)
176     {
177       error = strerror (errno);
178       fprintf (stderr, _("%s: invalid destination: %s\n"), dest, error);
179       return -1;
180     }
181
182   if (symlink (src, dest) == 0)
183     {
184       /* Destination must exist by now. */
185       if (access (dest, F_OK))
186         {
187           error = strerror (errno);
188           unlink (dest);
189           fprintf (stderr, _("Invalid link from \"%s\" to \"%s\": %s\n"),
190                    src, dest, error);
191           return 1;
192         }
193       return 0;
194     }
195   else
196     {
197       error = strerror (errno);
198       fprintf (stderr, _("Invalid link from \"%s\" to \"%s\": %s\n"),
199                src, dest, error);
200       return 1;
201     }
202 }