1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
/*
* realpath.c -- canonicalize pathname by removing symlinks
* Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library Public License for more details.
*/
#define resolve_symlinks
/*
* This routine is part of libc. We include it nevertheless,
* since the libc version has some security flaws.
*/
#include <limits.h> /* for PATH_MAX */
#ifndef PATH_MAX
#define PATH_MAX 8192
#endif
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "realpath.h"
#include "sundries.h" /* for xstrdup */
#define MAX_READLINKS 32
char *
myrealpath(const char *path, char *resolved_path, int maxreslth) {
int readlinks = 0;
char *npath;
char link_path[PATH_MAX+1];
int n;
char *buf = NULL;
npath = resolved_path;
/* If it's a relative pathname use getcwd for starters. */
if (*path != '/') {
if (!getcwd(npath, maxreslth-2))
return NULL;
npath += strlen(npath);
if (npath[-1] != '/')
*npath++ = '/';
} else {
*npath++ = '/';
path++;
}
/* Expand each slash-separated pathname component. */
while (*path != '\0') {
/* Ignore stray "/" */
if (*path == '/') {
path++;
continue;
}
if (*path == '.' && (path[1] == '\0' || path[1] == '/')) {
/* Ignore "." */
path++;
continue;
}
if (*path == '.' && path[1] == '.' &&
(path[2] == '\0' || path[2] == '/')) {
/* Backup for ".." */
path += 2;
while (npath > resolved_path+1 &&
(--npath)[-1] != '/')
;
continue;
}
/* Safely copy the next pathname component. */
while (*path != '\0' && *path != '/') {
if (npath-resolved_path > maxreslth-2) {
errno = ENAMETOOLONG;
goto err;
}
*npath++ = *path++;
}
/* Protect against infinite loops. */
if (readlinks++ > MAX_READLINKS) {
errno = ELOOP;
goto err;
}
/* See if last pathname component is a symlink. */
*npath = '\0';
n = readlink(resolved_path, link_path, PATH_MAX);
if (n < 0) {
/* EINVAL means the file exists but isn't a symlink. */
if (errno != EINVAL)
goto err;
} else {
#ifdef resolve_symlinks /* Richard Gooch dislikes sl resolution */
int m;
/* Note: readlink doesn't add the null byte. */
link_path[n] = '\0';
if (*link_path == '/')
/* Start over for an absolute symlink. */
npath = resolved_path;
else
/* Otherwise back up over this component. */
while (*(--npath) != '/')
;
/* Insert symlink contents into path. */
m = strlen(path);
if (buf)
free(buf);
buf = xmalloc(m + n + 1);
memcpy(buf, link_path, n);
memcpy(buf + n, path, m + 1);
path = buf;
#endif
}
*npath++ = '/';
}
/* Delete trailing slash but don't whomp a lone slash. */
if (npath != resolved_path+1 && npath[-1] == '/')
npath--;
/* Make sure it's null terminated. */
*npath = '\0';
if (buf)
free(buf);
return resolved_path;
err:
if (buf)
free(buf);
return NULL;
}
|