~ubuntu-branches/debian/experimental/sysvinit/experimental

« back to all changes in this revision

Viewing changes to debian/readlink.c

  • Committer: Bazaar Package Importer
  • Author(s): Petter Reinholdtsen
  • Date: 2009-09-13 00:13:49 UTC
  • Revision ID: james.westby@ubuntu.com-20090913001349-c4hnvhp0titxdpw9
Tags: 2.87dsf-5
* Uploading to experimental, to test the new build rules.

* Make sysv-rc postinst report detected problems to stderr too when
  failing to migrate.
* Fix typo in error message from postinst (Closes: #545409).
* Make initscripts depend on sysvinit-utils (>= 2.86.ds1-64), to
  make sure the fstab-decode program is available (Closes: #545356).
* Make sure the calls to 'update-rc.d X remove' in initscripts
  postinst do not ignore errors (Closes: #406361).
* Make sysvinit depend on sysvinit-utils (>= 2.86.ds1-66) to avoid
  that bootlogd disappear during partial upgrades (Closes: #545368).
* Restructure source package to make it possible to use debhelper in
  the common way to build the source, by moving debian/initscripts/
  and debian/sysv-rc/ into debian/src/.  Restructure build rules to
  use debhelper more, and migrate to debhelper 7.
* New patch 98_installtarget.patch to improve the sysvinit install
  target.
* Remove /etc/init.d/.depend.* in prerm, not postrm, to avoid
  surprises.
* Remove /var/lib/update-rc.d/* when the package is purged.
* Change cut-off point for the trimmed changelog entries in
  sysvinit-utils, initscripts and sysv-rc from version 2.84-3 to
  version 2.86.ds1-47, to reduce the package sizes.
* Drop hurd specific dependency on libc0.3 (>= 2.3.2.ds1-12).  It is
  no longer needed according to Michael Bunk.  Patch from Michael
  Biebl.
* Remove information about scripts in /var/lib/update-rc.d/ when
  their runlevel symlinks are removed (Closes: #545949).  Remove
  such files left behind earlier during upgrade.
* Bootlogd now starts as late as possible (Closes: #265801)
* Drop the binary /lib/init/readlink from initscripts and depend on
  coreutils (>= 5.93) instead.  Adjust scripts to use the program
  from coreutils from now on (Closes: #239342).
* Make sure insserv exit values propagate through update-rc.d to make
  sure packages with errors fail to install.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * readlink     Implementation of the readlink program that has a
3
 
 *              proper canonicalize function.
4
 
 *
5
 
 *              The canonicalized path is printed if the entire path
6
 
 *              exists, but _also_ if the last path element is missing
7
 
 *              and the path up till there is a directory.
8
 
 *              
9
 
 *              If you specify -f or --canonicalize twice (aka
10
 
 *              canonicalize-me-harder) then the "most canonical" path
11
 
 *              is printed. Even if a part of it doesn't exist, though
12
 
 *              the exit status will be non-zero in that case.
13
 
 *
14
 
 * realpath     If argv[0] is (^|/)realpath$, this program will behave
15
 
 *              as the realpath utility (-s not implemented).
16
 
 *              Note that 'readlink -ff' and 'realpath' are equivalent.
17
 
 *
18
 
 * Author:      Miquel van Smoorenburg.
19
 
 *
20
 
 * Version:     @(#)readlink  1.02  21-Mar-2004  miquels@cistron.nl
21
 
 *
22
 
 *              This file is part of the sysvinit suite,
23
 
 *              Copyright 2004 Miquel van Smoorenburg.
24
 
 *
25
 
 * License:     This program is free software; you can redistribute it and/or
26
 
 *              modify it under the terms of the GNU General Public License
27
 
 *              as published by the Free Software Foundation; either version
28
 
 *              2 of the License, or (at your option) any later version.
29
 
 *
30
 
 *              The realpath_sz() function in this file may also be
31
 
 *              redistributed under the terms of the LGPL:
32
 
 *
33
 
 */
34
 
 
35
 
#include <alloca.h>
36
 
#include <stdio.h>
37
 
#include <unistd.h>
38
 
#include <stdlib.h>
39
 
#include <string.h>
40
 
#include <malloc.h>
41
 
#include <errno.h>
42
 
#include <getopt.h>
43
 
#include <limits.h>
44
 
#include <sys/types.h>
45
 
#include <sys/stat.h>
46
 
#include <sys/param.h>
47
 
 
48
 
struct option readlink_options[] = {
49
 
  {  "canonicalize",    0, NULL, 'f' },
50
 
  {  "no-newline",      0, NULL, 'n' },
51
 
  {  "quiet",           0, NULL, 'q' },
52
 
  {  "silent",          0, NULL, 's' },
53
 
  {  "help",            0, NULL, 'h' },
54
 
  {  "verbose",         0, NULL, 'v' },
55
 
  {  "version",         0, NULL, 'V' },
56
 
  {  NULL,              0, NULL, 0   },
57
 
};
58
 
 
59
 
struct option realpath_options [] = {
60
 
  {  "no-newline",      0, NULL, 'n' },
61
 
  {  "help",            0, NULL, 'h' },
62
 
  {  "verbose",         0, NULL, 'v' },
63
 
  {  "version",         0, NULL, 'V' },
64
 
  {  NULL,              0, NULL, 0   },
65
 
};
66
 
 
67
 
static int system_path_max(char *name)
68
 
{
69
 
        int             path_max;
70
 
#ifdef PATH_MAX
71
 
        path_max = PATH_MAX;
72
 
#else
73
 
        if ((path_max = pathconf (name, _PC_PATH_MAX)) <= 0)
74
 
                path_max = 1024;
75
 
#endif
76
 
        return path_max;
77
 
}
78
 
 
79
 
/*
80
 
 *      Error. Try to build the path till here into resolved,
81
 
 *      return -1 if that works, -2 if failed (that means that
82
 
 *      the contents of "resolved" make no sense whatsoever).
83
 
 */
84
 
static int path_err(char *resolved, unsigned int reslen, char *restpath)
85
 
{
86
 
        if (strlen(resolved) + strlen(restpath) + 1 >= reslen) {
87
 
                errno = ENAMETOOLONG;
88
 
                return -2;
89
 
        }
90
 
        strcat(resolved, "/");
91
 
        strcat(resolved, restpath);
92
 
        return -1;
93
 
}
94
 
 
95
 
/*
96
 
 *      Ye olde realpath() functionne.
97
 
 *
98
 
 *      It takes an extra size argument for the resolved argument.
99
 
 *
100
 
 *      Exit status:     0 path resolved
101
 
 *                      -1 path not fully resolved (part not accessible)
102
 
 *                         but canonicalized as far as possible.
103
 
 *                      -2 error, "resolved" is invalid.
104
 
 *
105
 
 *      If exit status < 0 errno is set.
106
 
 */
107
 
static int realpath_sz(char *path, char *resolved, unsigned int reslen)
108
 
{
109
 
        struct stat     st;
110
 
        char            *p, *s;
111
 
        char            *buf, *buf2;
112
 
        int             c, n;
113
 
        int             exists = 1;
114
 
        int             link_max = _POSIX_LINK_MAX;
115
 
        int             links = 0;
116
 
        int             st_mode;
117
 
 
118
 
        /*
119
 
         *      Some basic checks and allocations.
120
 
         */
121
 
        if (strlen(path) + 2 >= reslen) {
122
 
                errno = ENAMETOOLONG;
123
 
                return -2;
124
 
        }
125
 
        if ((buf = alloca(reslen)) == NULL)
126
 
                return -2;
127
 
        if ((buf2 = alloca(reslen)) == NULL)
128
 
                return -2;
129
 
 
130
 
        /*
131
 
         *      Start with the current working directory
132
 
         *      if the path is relative.
133
 
         */
134
 
        if (*path != '/') {
135
 
                if (getcwd(resolved, reslen - 1) == NULL) {
136
 
                        if (errno == ERANGE)
137
 
                                errno = ENAMETOOLONG;
138
 
                        return -2;
139
 
                }
140
 
                strcpy(buf2, "/");
141
 
                strcat(buf2, path);
142
 
                path = buf2;
143
 
        } else {
144
 
                strcpy(resolved, "/");
145
 
        }
146
 
 
147
 
        st.st_mode = S_IFDIR;
148
 
 
149
 
        while (1) {
150
 
 
151
 
                /*
152
 
                 *      Skip multiple preceding slashes.
153
 
                 */
154
 
                while (*path == '/' && path[1] == '/') path++;
155
 
 
156
 
                /*
157
 
                 *      At this point, "resolved" contains the path up
158
 
                 *      till here without a trailing slash, and "path"
159
 
                 *      contains the rest of the path starting with a /.
160
 
                 *
161
 
                 *      One exception: if path is empty, then resolved
162
 
                 *      can contain a single trailing slash (happens if
163
 
                 *      path was just "/" in the previous round)
164
 
                 */
165
 
                st_mode = st.st_mode;
166
 
                st.st_mode = S_IFREG;
167
 
                if (exists > 0 && lstat(resolved, &st) < 0) {
168
 
                        if (errno != ENOENT && errno != ENOTDIR &&
169
 
                            errno != EACCES)
170
 
                                return path_err(resolved, reslen, path);
171
 
                        exists--;
172
 
                        if (errno != ENOENT) exists--;
173
 
                }
174
 
 
175
 
                /*
176
 
                 *      Remove trailing slash if this is a directory.
177
 
                 */
178
 
                if ((s = strrchr(resolved, '/')) != NULL && s[1] == 0 &&
179
 
                     s > resolved && S_ISDIR(st.st_mode))
180
 
                        *s = 0;
181
 
 
182
 
                if (exists > 0 && S_ISLNK(st.st_mode)) {
183
 
                        /*
184
 
                         *      It's a link. Prepend the rest of the
185
 
                         *      remaining path with the contents of
186
 
                         *      the link, remove the last element
187
 
                         *      from resolved, and loop.
188
 
                         */
189
 
                        if (++links > link_max) {
190
 
                                errno = ELOOP;
191
 
                                return path_err(resolved, reslen, path);
192
 
                        }
193
 
 
194
 
                        if ((n = readlink(resolved, buf, reslen - 1)) < 0)
195
 
                                return path_err(resolved, reslen, path);
196
 
                        buf[n] = 0;
197
 
 
198
 
                        if (buf[0] == '/')
199
 
                                p = resolved + 1;
200
 
                        else {
201
 
                                p = strrchr(resolved, '/');
202
 
                                if (p == resolved) p++;
203
 
                        }
204
 
                        c = *p;
205
 
                        *p = 0;
206
 
                        if (strlen(resolved) + strlen(buf) +
207
 
                            strlen(path) + 3 >= reslen) {
208
 
                                *p = c;
209
 
                                return path_err(resolved, reslen, path);
210
 
                        }
211
 
                        if (*path)
212
 
                                strcat(buf, path);
213
 
                        buf2[0] = 0;
214
 
                        if (buf[0] != '/') strcat(buf2, "/");
215
 
                        strcat(buf2, buf);
216
 
                        path = buf2;
217
 
 
218
 
                        continue;
219
 
                }
220
 
 
221
 
                /*
222
 
                 *      Are we done?
223
 
                 */
224
 
                if (*path == 0)
225
 
                        break;
226
 
 
227
 
                /*
228
 
                 *      repeating /./ sequences can be skipped.
229
 
                 */
230
 
                while (path[0] == '/' && path[1] == '.' && path[2] == '/') {
231
 
                        path += 2;
232
 
                        while (*path == '/' && path[1] == '/') path++;
233
 
                }
234
 
 
235
 
                /*
236
 
                 *      Is it a trailing / or /. or /./ ?
237
 
                 */
238
 
                if (exists > 0 && (path[1] == 0 ||
239
 
                    (path[1] == '.' && (path[2] == 0 || path[2] == '/')))) {
240
 
                        
241
 
                        if (S_ISDIR(st.st_mode)) {
242
 
                                /*
243
 
                                 *      Skip . or ./
244
 
                                 */
245
 
                                path++;
246
 
                                if (*path == '.') path++;
247
 
                                continue;
248
 
                        }
249
 
 
250
 
                        /*
251
 
                         *      Yup, ".", but that doesn't work if the
252
 
                         *      parent path isn't a directory.
253
 
                         */
254
 
                        errno = ENOTDIR;
255
 
                        exists = -1;
256
 
                }
257
 
 
258
 
                /*
259
 
                 *      Is it ../ ?
260
 
                 */
261
 
                if (exists > 0 && path[1] == '.' && path[2] == '.' &&
262
 
                              (path[3] == 0 || path[3] == '/')) {
263
 
 
264
 
                        if (S_ISDIR(st.st_mode)) {
265
 
                                /*
266
 
                                 *      Back up one element.
267
 
                                 */
268
 
                                p = strrchr(resolved, '/');
269
 
                                if (p == resolved) p++;
270
 
                                *p = 0;
271
 
                                path += 3;
272
 
                                continue;
273
 
                        }
274
 
 
275
 
                        /*
276
 
                         *      Yup, "..", but that doesn't work if the
277
 
                         *      parent path isn't a directory.
278
 
                         */
279
 
                        errno = ENOTDIR;
280
 
                        exists = -1;
281
 
                }
282
 
 
283
 
                /*
284
 
                 *      Okay add this element to the resolved path.
285
 
                 */
286
 
                for (p = path + 1; *p && *p != '/'; p++)
287
 
                        ;
288
 
                if (strlen(resolved) + (p - path) + 2 >= reslen) {
289
 
                        errno = ENAMETOOLONG;
290
 
                        return -2;
291
 
                }
292
 
                c = *p;
293
 
                *p = 0;
294
 
                if (resolved[0] == '/' && resolved[1] == 0) path++;
295
 
                strcat(resolved, path);
296
 
                *p = c;
297
 
                path = p;
298
 
        }
299
 
 
300
 
        return (exists > 0 || (exists == 0 && S_ISDIR(st_mode))) ? 0 : -1;
301
 
}
302
 
 
303
 
void usage(char *progname)
304
 
{
305
 
        fprintf(stderr, "Usage: %s: [OPTION]... FILE\n", progname);
306
 
        exit(1);
307
 
}
308
 
 
309
 
void version(char *progname)
310
 
{
311
 
        printf("%s version 1.0 (sysvinit 2.85)\n", progname);
312
 
        exit(1);
313
 
}
314
 
 
315
 
char *mystrerror(int e)
316
 
{
317
 
        if (e == EINVAL)
318
 
                return "Not a symbolic link";
319
 
        else
320
 
                return strerror(e);
321
 
}
322
 
 
323
 
int cmd_readlink(int argc, char **argv, char *progname)
324
 
{
325
 
        char            *buf;
326
 
        char            *path;
327
 
        int             c, l, e;
328
 
        int             opt_canon = 0;
329
 
        int             opt_nonl = 0;
330
 
        int             opt_verbose = 0;
331
 
        unsigned int    path_max;
332
 
 
333
 
        while ((c = getopt_long(argc, argv, "fnqsv", readlink_options, NULL)) != EOF) switch(c) {
334
 
                case 'f':
335
 
                        opt_canon++;
336
 
                        break;
337
 
                case 'n':
338
 
                        opt_nonl++;
339
 
                        break;
340
 
                case 'q':
341
 
                case 's':
342
 
                        opt_verbose = 0;
343
 
                        break;
344
 
                case 'v':
345
 
                        opt_verbose = 1;
346
 
                        break;
347
 
                case 'V':
348
 
                        version(progname);
349
 
                        break;
350
 
                default:
351
 
                        usage(progname);
352
 
                        break;
353
 
        }
354
 
 
355
 
        if (optind != argc - 1)
356
 
                usage(progname);
357
 
        path = argv[optind];
358
 
 
359
 
        /*
360
 
         *      Rules on printing stdout, stderr and exit status:
361
 
         *
362
 
         *      simple readlink: succes: print value on stdout, exit 0
363
 
         *                       fail:   print error on stderr, exit 1
364
 
         *
365
 
         *      canonicalize:    succes: print path on stdout, exit 0
366
 
         *                       fail:   print path on stdout if -ff
367
 
         *                               print error on stderr if verbose
368
 
         *                               exit 1 if path was still canonicalized
369
 
         *                               exit 254 if not completely canon'ed
370
 
         */
371
 
 
372
 
        l = opt_canon ? -2 : -1;
373
 
 
374
 
        path_max = system_path_max(path);
375
 
        if ((buf = alloca(path_max)) != NULL) {
376
 
                if (opt_canon) {
377
 
                        l = realpath_sz(path, buf, path_max);
378
 
                } else {
379
 
                        l = readlink(path, buf, path_max - 1);
380
 
                        if (l >= 0)
381
 
                                buf[l] = 0;
382
 
                        else
383
 
                                l = -2;
384
 
                }
385
 
        }
386
 
 
387
 
        e = errno;
388
 
 
389
 
        if (l >= 0 || opt_canon > 1)
390
 
                printf("%s%s", (l == -2) ? path : buf, opt_nonl ? "" : "\n");
391
 
 
392
 
        if (l < 0 && opt_verbose)
393
 
                fprintf(stderr, "%s: %s: %s\n", progname,
394
 
                        (l == -2) ? path : buf, mystrerror(e));
395
 
 
396
 
        if (l < 0) return (l == -2) ? 254 : 1;
397
 
        return 0;
398
 
}
399
 
 
400
 
int cmd_realpath(int argc, char **argv, char *progname)
401
 
{
402
 
        char            *buf;
403
 
        char            *path;
404
 
        int             c, l, e;
405
 
        int             opt_nonl = 0;
406
 
        int             opt_verbose = 0;
407
 
        int             path_max;
408
 
 
409
 
        while ((c = getopt_long(argc, argv, "nv", realpath_options, NULL)) != EOF) switch(c) {
410
 
                case 'n':
411
 
                        opt_nonl++;
412
 
                        break;
413
 
                case 'v':
414
 
                        opt_verbose = 1;
415
 
                        break;
416
 
                case 'V':
417
 
                        version(progname);
418
 
                        break;
419
 
                default:
420
 
                        usage(progname);
421
 
                        break;
422
 
        }
423
 
 
424
 
        if (optind != argc - 1)
425
 
                usage(progname);
426
 
        path = argv[optind];
427
 
 
428
 
        path_max = system_path_max(path);
429
 
        if ((buf = alloca(path_max)) != NULL)
430
 
                l = realpath_sz(path, buf, path_max);
431
 
        else
432
 
                l = -2;
433
 
 
434
 
        e = errno;
435
 
 
436
 
        printf("%s%s", (l == -2) ? path : buf, opt_nonl ? "" : "\n");
437
 
 
438
 
        if (l < 0 && opt_verbose)
439
 
                fprintf(stderr, "%s: %s: %s\n", progname,
440
 
                        (l == -2) ? path : buf, mystrerror(e));
441
 
 
442
 
        if (l < 0) return (l == -2) ? 254 : 1;
443
 
        return 0;
444
 
}
445
 
 
446
 
int main(int argc, char **argv)
447
 
{
448
 
        char            *p;
449
 
 
450
 
        if ((p = strrchr(argv[0], '/')) != NULL)
451
 
                p++;
452
 
        else
453
 
                p = argv[0];
454
 
 
455
 
        if (strcmp(p, "realpath") == 0)
456
 
                return cmd_realpath(argc, argv, p);
457
 
 
458
 
        return cmd_readlink(argc, argv, "readlink");
459
 
}
460