Mail Archives: djgpp-workers/2003/01/16/10:19:42
Hello.
I mentioned a while ago that __solve_symlinks() doesn't work
with relative symlinks that have too many ".."s, i.e.: when they
have more ".."s than required to reach the root directory.
(It's possible that it also does not work with symlinks to a file
in a directory accessed via a symlink to the directory. E.g.:
mkdir foo
touch foo/bar
ln -s foo baz
cat baz/foo
I've been through a few iterations of patching __solve_symlinks,
so perhaps it works. I'm pretty sure that it fails, though.)
On Unix "/.." is equivalent to "/". This is not true on DOS.
We have to emulate this behaviour using _fixpath(), which removes
excess ".."s.
I tried to fix the current implementation, to cope with ".."s, but
I hit some problems:
* How do you know how many directory levels are present? If you have
a path like /dev/env/DJDIR, then you need to expand it, to find out
how many directory levels are present.
* How do you collapse ".."s. You could call _fixpath(), but you have to be
careful - that may over-optimise and remove too many ".."s. You can only
call _fixpath() on what you have resolved so far.
Anyway, with these questions in mind, I tried to fix the current
__solve_symlinks implementation. I found the current code a bit difficult
to work with, so in the end I rewrote it. Below is the patch.
Reasons why you might not like the patch:
* It's a complete rewrite.
* It doesn't cope with UNCs (\\machine\path).
* It's probably slower, since it calls _put_path a lot.
* It probably tidies up filenames more than necessary.
* It needs to check for buffer overflows more.
* It uses goto. ;) (This makes the code more readable IMNSHO.)
Reasons to like the patch:
* It handles all the symlink cases I've thrown at it.
I spent a few hours debugging weird cases, like: c:../../../foo,
/dev/env/DJDIR/../../../../foo.
* fileutils still works. I didn't see any regressions in the test suite.
Things you should ignore in the patch:
* __partially_canonicalize_path - This is cruft from a previous iteration
of the patch, which I haven't clean up yet.
Below is a test script. It should all work, when you've rebuild fileutils
and textutils with the patch below.
---Start test-sym.sh---
#!/bin/bash
CAT=${CAT-cat}
LN=${LN-ln}
MKDIR=${MKDIR-mkdir}
if [ "a$1" = "a-x" ]; then
set -x
shift
fi
# Chain of symlinks
$LN -fnsv b a
$LN -fnsv c b
$LN -fnsv d c
echo foo > d
$CAT d
# Relative symlink
$MKDIR -p t-dir
$LN -fnsv ../d t-dir/d
$CAT t-dir/d
# Too many dots
here=$(pwd)
dotty=$(echo $here | sed -e 's:/:/\.\./\.\./\.\./\.\./\.\./:')
$LN -fnsv ${dotty}/d e
$CAT e
# If this directory is under /dev/env/DJDIR, try putting that in a symlink.
drive=$(echo $here | sed -e 's!^\(.:\).*!\1!')
wibble=$(echo $here | sed -e "s!^$DJDIR!/dev/env/DJDIR!")
if [ "a$wibble" != "a" ]; then
$LN -fnsv ${wibble}/d f
$CAT f
if [ "a$drive" != "a" ]; then
# Chuck in an extra slash, to spice things up.
$LN -fnsv ${drive}/${wibble}/d g
$CAT g
fi
fi
---End test-sym.sh---
I'd appreciate any comments on the patch. If anyone's feeling brave,
please test it too. This doesn't affect just symlinks - it affects
all files.
Bye, Rich =]
Index: src/libc/compat/unistd/xsymlink.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/xsymlink.c,v
retrieving revision 1.6
diff -p -c -3 -r1.6 xsymlink.c
*** src/libc/compat/unistd/xsymlink.c 23 Dec 2002 11:44:49 -0000 1.6
--- src/libc/compat/unistd/xsymlink.c 16 Jan 2003 09:53:12 -0000
***************
*** 1,18 ****
/* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
! /* Written by Laurynas Biveinis */
/* Internal source file specifying DJGPP symlink prefix and internal */
/* function which fully resolves given symlink. (Function readlink() */
/* resolves only last filename component and one symlink level.) */
#include <libc/stubs.h>
#include <libc/symlink.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
- #include <unistd.h>
#include <string.h>
#include "xsymlink.h"
--- 1,30 ----
+ /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
! /* Written by Laurynas Biveinis. Rewritten by Richard Dawe. */
/* Internal source file specifying DJGPP symlink prefix and internal */
/* function which fully resolves given symlink. (Function readlink() */
/* resolves only last filename component and one symlink level.) */
+ /* TODO: Audit the code for places where the FILENAME_MAX buffers
+ * could overflow. */
+
+ /* TODO: Should this code preserve UNC paths? Probably. */
+
#include <libc/stubs.h>
#include <libc/symlink.h>
+ #include <libc/dosio.h>
+ #include <libc/unconst.h>
#include <errno.h>
#include <limits.h>
+ #include <stddef.h>
#include <stdio.h>
#include <string.h>
+ #include <sys/stat.h>
+ #include <assert.h>
+ #include <dpmi.h>
+ #include <go32.h>
#include "xsymlink.h"
***************
*** 21,180 ****
*/
#define MAX_SYMLINK 8
! static void advance(char ** s, char ** e);
! int __solve_symlinks(const char * __symlink_path, char * __real_path)
{
! int bytes_copied;
! char * start;
! char * end;
! int old_errno;
! char fn_buf[FILENAME_MAX + 1];
! char resolved[FILENAME_MAX + 1];
! int link_level = 0;
! int found_absolute_path;
! int done_something;
! char * ptr;
! int non_trivial;
!
! if (!__symlink_path || !__real_path)
! {
! errno = EINVAL;
! return 0;
! }
!
! if (strlen(__symlink_path) > FILENAME_MAX)
! {
! errno = ENAMETOOLONG;
! return 0;
! }
!
! strcpy(__real_path, __symlink_path);
! start = __real_path;
! end = strpbrk(__real_path, "/\\");
! if (!end)
! end = __real_path + strlen(__real_path);
! while (start && *start)
! {
! /* Extract path component we will be resolving */
! strcpy(resolved, __real_path);
! if (*end)
! resolved[end - __real_path] = '\0';
! old_errno = errno;
! found_absolute_path = 0;
! done_something = 0;
! non_trivial = 0;
! /* Resolve that component. Repeat until we encounter non-symlink,
! or not trivial symlink (in form 'dir/file'). */
! do
! {
! bytes_copied = __internal_readlink(resolved, 0, fn_buf, FILENAME_MAX);
! if (bytes_copied != -1)
! {
! done_something = 1;
! link_level++;
! fn_buf[bytes_copied] = '\0';
! strcpy(resolved, fn_buf);
! /* FIXME: does absolute path check below work with chroot()? */
! if (((bytes_copied > 2) && (resolved[1] == ':')) ||
! ((bytes_copied > 0) && ((resolved[0] == '/') ||
! (resolved[0] == '\\'))))
! {
! found_absolute_path = 1;
! }
! /* If we found dir/file, do not iterate */
! else
! {
! if ((resolved[0] == '.') &&
! ((resolved[1] == '/') || (resolved[1] == '\\')))
! ptr = resolved + 2;
! else
! ptr = resolved;
! while (ptr < resolved + strlen(resolved)) /* Skip last char */
! if ((*ptr == '/') || (*ptr == '\\'))
! {
! bytes_copied = -1;
! non_trivial = 1;
! break;
! }
! else
! ptr++;
! }
! }
! } while ((bytes_copied != -1) && (link_level <= MAX_SYMLINK));
! if (link_level > MAX_SYMLINK)
! {
! errno = ELOOP;
! return 0;
! }
! else
! errno = old_errno;
! if (done_something)
! {
! /* If it wasn't the last path component resolved, save the
! * unresolved tail for future
! */
! if (*end)
! strcpy(fn_buf, end);
! else
! fn_buf[0] = '\0';
! if (found_absolute_path)
! {
! /* Discard already resolved part, because symlink's target */
! /* is absolute path */
! strcpy(__real_path, resolved);
! }
! else
! {
! /* Add resolved symlink component to already resolved part */
! memcpy(start, resolved, strlen(resolved) + 1);
! }
! /* Append saved tail for further processing */
! if (fn_buf[0])
! strcat(__real_path, fn_buf);
}
! if (done_something)
! {
! if (found_absolute_path)
! {
! /* Restart processing. God knows what's in the new absolute path */
! start = __real_path;
! end = strpbrk(__real_path, "/\\");
! }
! else if (non_trivial)
! {
! /* If we got component like 'dir/file', start resolving from */
! /* dir. */
! end = strpbrk(start + 1, "/\\");
! }
! else
! {
! /* Resolve next component */
! advance(&start, &end);
! }
}
else
! {
! /* Resolve next component */
! advance(&start, &end);
}
! if (!end)
! end = __real_path + strlen(__real_path);
! }
! return 1;
! }
!
! /* Advance to the next portion of the path. Cope with multiple slashes. */
! static void advance(char ** s, char ** e)
! {
! *s = strpbrk(*s + 1, "/\\");
! if (*s)
! {
! while ((**s == '/') || (**s == '\\'))
! (*s)++;
! }
!
! while ((**e == '/') || (**e == '\\'))
! (*e)++;
! *e = strpbrk(*e, "/\\");
}
--- 33,661 ----
*/
#define MAX_SYMLINK 8
! static inline int
! is_slash (int ch)
! {
! return((ch == '\\') || (ch == '/'));
! }
! static inline int
! has_drive (const char *path)
{
! return(path[0] && path[1] == ':');
! }
!
! static inline int
! is_absolute (const char *path)
! {
! return(is_slash(path[0]) || (has_drive(path) && is_slash(path[2])));
! }
!
! static inline int
! has_trailing_slash (char *path)
! {
! return(is_slash(path[strlen(path) - 1]));
! }
!
! static inline void
! add_trailing_slash (char *path)
! {
! if (!has_trailing_slash(path))
! strcat(path, "/");
! }
!
! static inline void
! eat_trailing_slash (char *path)
! {
! size_t endpos = strlen(path) - 1;
! char *p = strchr(path, '/');
!
! /* The slash in / or c:/ is not considered to be a trailing slash. */
! if (is_slash(path[endpos]) && (&path[endpos] != p))
! path[endpos] = '\0';
! }
!
! /*
! * put_path_wrapper: _put_path on 'in'; result into 'out'. 'in' and 'out'
! * can point to the same buffer.
! */
!
! static int
! put_path_wrapper (const char *in, char *out)
! {
! int ret = _put_path(in);
!
! dosmemget(__tb, FILENAME_MAX, out);
! return(ret);
! }
!
! /*
! * tidy_up_path: Collapse consecutive slashes to one. Convert slashes
! * to forward slashes. Eliminate occurrences of '.'.
! */
!
! static void
! tidy_up_path (const char *in, char *out)
! {
! int in_pos = 0;
! int out_pos = 0;
!
! /* Copy the drive & root over, if present. We do this here, to avoid
! * it getting munged by the "/./" collapsing code. */
! if (has_drive(in)) {
! out[out_pos] = in[in_pos];
! out[out_pos + 1] = in[in_pos + 1];
! in_pos += 2;
! out_pos += 2;
! }
!
! if (is_absolute(in)) {
! out[out_pos] = in[in_pos];
! in_pos++;
! out_pos++;
! }
!
! /* Eat any './' at the start. */
! while (1) {
! if (strstr(&in[in_pos], "./") == &in[in_pos])
! in_pos += 2;
! else
! break;
! }
!
! for (; in[in_pos] != '\0'; in_pos++) {
! while (is_slash(in[in_pos])) {
! /* Eat consecutive slashes. */
! if (is_slash(in[in_pos + 1])) {
! in_pos++;
! continue;
}
!
! /* Collapse "/./" to "/". */
! if ((in[in_pos + 1] == '.') && (is_slash(in[in_pos + 2]))) {
! in_pos += 2;
! continue;
}
+
+ break;
+ }
+
+ /* Convert backward-slashes to forward-slashes. */
+ if (is_slash(in[in_pos]))
+ out[out_pos] = '/';
+ else
+ out[out_pos] = in[in_pos];
+
+ out_pos++;
+ }
+
+ out[out_pos] = '\0';
+ eat_trailing_slash(out);
+
+ /* Eat any '/.' at the end. */
+ while (1) {
+ size_t endpos = strlen(out);
+
+ /* Leave '.' if that's all that's there. */
+ if (endpos < 3)
+ break;
+
+ endpos--;
+ if ((is_slash(out[endpos - 1]) && (out[endpos] == '.')))
+ out[endpos - 1] = '\0';
+ else
+ break;
+ }
+ }
+
+ static int
+ get_current_drive (void)
+ {
+ __dpmi_regs r;
+
+ r.h.ah = 0x19;
+ __dpmi_int(0x21, &r);
+ return(r.h.al);
+ }
+
+ static char *
+ strrpbrk (const char *str, const char *set)
+ {
+ char *rightmost = NULL;
+ char *p;
+
+ for (; *set; set++) {
+ p = strrchr(str, *set);
+ if (p > rightmost)
+ rightmost = unconst(p, char *);
+ }
+
+ return(rightmost);
+ }
+
+ /*
+ * collapse_dirs_and_dotdots: Simplify paths by eliminating directories
+ * according to the number of leading '..'s.
+ *
+ * Count how many '../' prefixes there are in suffix - n. Eat n trailing
+ * directories from prefix and n leading '..' from suffix. Don't go below
+ * the root directory.
+ *
+ * ASSUMPTIONS:
+ *
+ * . prefix is an absolute path - with or without drive letter.
+ */
+ static void
+ collapse_dirs_and_dotdots (char *prefix, char *suffix)
+ {
+ char *p, *q;
+ int prefix_start = 0; /* Don't eat path parts beyond this point. */
+ int no_slash_after = 0; /* No slash after the dot-dots? */
+ int n = 0;
+ int i;
+
+ assert(prefix && suffix);
+
+ p = strstr(suffix, "..");
+ if (p != suffix)
+ return;
+
+ do {
+ /* Move on to next '..', if any. */
+ p += 2;
+ if (*p) {
+ if (is_slash(*p))
+ break;
else
! p++;
! }
!
! n++;
!
! q = strstr(p, "..");
! if ((p != NULL) && (p != q))
! break;
! } while (p != NULL);
!
! if (!n)
! return;
!
! /* Eat path parts in prefix. */
! eat_trailing_slash(prefix);
!
! if (prefix[0] && (prefix[1] == ':'))
! prefix_start = 2;
! else
! prefix_start = 0;
!
! for (i = 0; i < n; i++) {
! p = strrpbrk(prefix, "\\/");
! if (p == NULL)
! continue;
!
! if (p == &prefix[prefix_start]) {
! /* We've reached the root directory. Eat everything after it. */
! if (p[1])
! p[1] = '\0';
! } else {
! *p = '\0';
! }
! }
!
! /* Eat dot-dots in suffix. If there's slash at the end
! * of the matched prefixes, it means there's nothing after the dot-dots
! * and we should eat all of suffix. */
! eat_trailing_slash(suffix);
!
! no_slash_after = (suffix[(3 * n) - 1] == '\0');
!
! if (no_slash_after)
! suffix[0] = '\0';
! else
! memmove(suffix, suffix + (3 * n), strlen(suffix + (3 * n)) + 1);
! }
!
! /*
! * get_cwd_and_collapse: For any path 'in', make it absolute
! * with a drive specifier. Eliminate any leading '..'s from 'in'
! * by removing the preceeding directories in the absolute path.
! * 'in' and 'out' can be the same buffer.
! */
!
! static void
! get_cwd_and_collapse (const char *in, char *out)
! {
! char prefix[FILENAME_MAX], suffix[FILENAME_MAX];
! int drive;
!
! tidy_up_path(in, suffix);
!
! if (has_drive(suffix) && !is_absolute(suffix)) {
! /* c:foo */
! /* Relative path with drive => get the drive,
! * so we can get the working directory. */
! if ((suffix[0] >= 'a') && (suffix[0] <= 'z'))
! drive = suffix[0] - 'a';
! else
! drive = suffix[0] - 'A';
!
! memcpy(prefix, suffix, 2);
! prefix[2] = '\0';
!
! __get_current_directory(prefix + 2, drive);
!
! /* Eat drive, now it's in prefix. */
! memmove(suffix, suffix + 2, strlen(suffix + 2) + 1);
! } else if (has_drive(suffix) && is_absolute(suffix)) {
! /* c:/foo */
! /* Split the path on the slash after the drive specifier.
! * Then we can collapse any leading '..'s after the drive specifier. */
! strncpy(prefix, suffix, 3);
! prefix[3] = '\0';
! memmove(suffix, suffix + 3, strlen(suffix + 3) + 1);
! } else if (!has_drive(suffix) && is_absolute(suffix)) {
! /* /foo */
! /* Get the drive into prefix, add a slash, shift suffix to allow
! * collapse of any leading '..'s after the slash. */
! drive = get_current_drive();
!
! prefix[0] = drive + (drive < 26 ? 'a' : 'A');
! prefix[1] = ':';
! prefix[2] = '/';
! prefix[3] = '\0';
!
! memmove(suffix, suffix + 1, strlen(suffix + 1) + 1);
! } else {
! /* foo */
! /* Get the current working directory as the prefix. This is enough
! * allow collapsing of any leading '..'s. */
! drive = get_current_drive();
!
! prefix[0] = drive + (drive < 26 ? 'a' : 'A');
! prefix[1] = ':';
! prefix[2] = '\0';
!
! __get_current_directory(prefix + 2, drive);
! }
!
! /* Eliminate any leading '..'s. */
! collapse_dirs_and_dotdots(prefix, suffix);
!
! /* Reconstruct the path. */
! strcpy(out, prefix);
! if (strlen(suffix)) {
! if (!is_slash(out[strlen(out) - 1]))
! strcat(out, "/");
! strcat(out, suffix);
! }
! }
!
! /*
! * __solve_symlinks: Given a path, resolve all the symlinks in it,
! * to give the real path.
! *
! * ALGORITHM:
! *
! * Maintain a string containing what has been resolved so far. Work from
! * left to right. For each part, perform a symlink lookup
! * on the concatentation of resolved-so-far and the part.
! *
! * If the lookup fails, because the part is not a symlink, add it
! * to resolved-so-far. Carry on resolving.
! *
! * If the lookup succeeds, then there are couple of cases:
! *
! * . For absolute symlinks, discard what has been resolved so far.
! * Restart the resolution process with the concatenation
! * of the lookup result and whatever is remaining from the original path.
! *
! * . For relative symlinks, restart the resolution process
! * with the concatenation of resolved-so-far, the lookup result
! * and whatever is remaining from the original path.
! *
! * We need to restart the resolution process, because the target
! * of the symlink may be a symlink. Where we have to restart depends
! * on the symlink type: absolute => completely restart;
! * relative => at the first part of the lookup result.
! *
! * COMPLICATIONS:
! *
! * . There are special paths such as /dev/env/DJDIR.
! *
! * . "/.." is equivalent to "/" on Unix, but not on DOS.
! * So we have to emulate this by collapsing "..". This can only be done
! * between the end of the resolved path and the start
! * of the lookup result, otherwise we may collapse parts of the path
! * that are actually symlinks. (This means we cannot use _fixpath().)
! */
!
! int
! __solve_symlinks (const char * __symlink_path, char * __real_path)
! {
! int old_errno = errno;
!
! /* Initially this is __symlink_path after a little tidying up.
! * Later this may be partially-resolved, when we restart resolution. */
! char my_symlink[FILENAME_MAX] = "";
!
! /* What we've resolved of __symlink_path so far. */
! char resolved[FILENAME_MAX] = "";
!
! /* The current part of my_symlink that we're trying to resolve. */
! char part[FILENAME_MAX] = "";
!
! /* Buffer and results for/from __internal_readlink calls. */
! char readlink_buf[FILENAME_MAX];
! int readlink_res;
!
! /* Temporary buffer. */
! char buf[FILENAME_MAX];
!
! char *start, *end;
!
! int link_level = 0;
!
! if (!__symlink_path || !__real_path) {
! errno = EINVAL;
! return(0);
! }
!
! if (strlen(__symlink_path) >= FILENAME_MAX) {
! errno = ENAMETOOLONG;
! return(0);
! }
!
! /* Collapse any '..'s and fix up special names like /dev/env/DJDIR. */
! get_cwd_and_collapse(__symlink_path, my_symlink);
! put_path_wrapper(my_symlink, buf);
! tidy_up_path(buf, my_symlink);
!
! /* Iterate over each part of the path. */
!
! /* ASSUMPTION: my_symlink is now a drive-qualified absolute path.
! * Skip over the drive and its root directory. */
! start = my_symlink + 3;
! memcpy(resolved, my_symlink, 3);
! resolved[3] = '\0';
! end = strpbrk(start, "\\/");
!
! do {
! ptrdiff_t diff = 0;
!
! /* Cope if this is the last part of the path. */
! if (end == NULL)
! end = start + strlen(start);
!
! diff = end - start;
! memcpy(part, start, diff);
! part[diff] = '\0';
!
! /* If this part is a '..', then we can eliminate it. */
! if (strcmp(part, "..") == 0) {
! collapse_dirs_and_dotdots(resolved, part);
! goto next_part;
! }
!
! /*
! * Resolve this part. Consider the different types of path that can
! * result, if this is a symlink:
! *
! * (1) Absolute with or without a DOS drive: We discard everything
! * that has been resolved previously and start with the result.
! *
! * (2) Relative: We add the result to the previously resolved path.
! *
! * (3) Relative with a DOS drive: This is treated the same way as (1).
! * It's just slightly less absolute.
! *
! * Since any part of the result from the lookup can be a symlink,
! * we have to resume the matching there.
! */
!
! strcpy(buf, resolved);
! add_trailing_slash(buf);
! strcat(buf, part);
!
! readlink_res
! = __internal_readlink(buf, 0, readlink_buf, sizeof(readlink_buf));
!
! if (readlink_res >= 0) {
! /* Symlink */
! link_level++;
!
! if (link_level > MAX_SYMLINK) {
! errno = ELOOP;
! return(0);
! }
!
! readlink_buf[readlink_res] = '\0';
! } else {
! /* Probably not a symlink - a regular file or directory? */
! /* Just update resolved and soldier on. */
! strcpy(resolved, buf);
! goto next_part;
! }
!
! tidy_up_path(readlink_buf, buf);
!
! if (is_absolute(buf) || has_drive(buf)) {
! /* Absolute => just start with this path. */
! get_cwd_and_collapse(buf, resolved);
! } else {
! /* Relative => prefix with resolved path up to now. */
! collapse_dirs_and_dotdots(resolved, buf);
! if (buf[0]) {
! add_trailing_slash(resolved);
! strcat(resolved, buf);
}
! }
!
! /* Append unresolved parts, if any. */
! if (end && *end)
! strcat(resolved, end);
!
! /* Fix up any special paths like /dev/env/DJDIR that may have appeared. */
! put_path_wrapper(resolved, resolved);
! tidy_up_path(resolved, my_symlink);
!
! /* Restart the resolution. */
! /* ASSUMPTION: my_symlink is now a drive-qualified absolute path.
! * Point end at the root directory, so next_part will go to the first
! * non-root part. */
! memcpy(resolved, my_symlink, 3);
! resolved[3] = '\0';
!
! start = NULL;
! end = my_symlink + 2;
!
! /* Find the next part of the path, if any. */
! next_part:
! start = end;
! if (start && *start) {
! start++;
! end = strpbrk(start, "\\/");
! } else {
! start = end = NULL;
! }
! } while (start != NULL);
!
! strcpy(__real_path, resolved);
! errno = old_errno;
!
! return(1);
! }
!
! #ifdef TEST
!
! #include <stdlib.h>
!
! int
! main (int argc, char *argv[])
! {
! typedef struct {
! char *prefix;
! char *suffix;
! } collapse_dirs_and_dotdots_t;
!
! const collapse_dirs_and_dotdots_t cddd_tests[] = {
! { "c:/", ".." },
! { "c:/", "../.." },
! { "c:/", "../../../" },
! { "/", ".." },
! { "/", "../.." },
! { "/", "../../../" },
! { "c:/djgpp", ".." },
! { "c:/djgpp", "../.." },
! { "c:/djgpp", "../../../" },
! { "/djgpp", ".." },
! { "/djgpp", "../.." },
! { "/djgpp", "../../../" },
! { "c:/really/really/really/really/long/path", ".." },
! { "c:/really/really/really/really/long/path", "../.." },
! { "c:/really/really/really/really/long/path", "../../../.." },
! { "c:/really/really/really/really/long/path", "../../../../.." },
! { "c:/really/really/really/really/long/path", "../../../../../.." },
! { "c:/really/really/really/really/long/path", "../../../../../../.." },
! { "c:/really/really/really/really/long/path", "../../../../../../../.." },
! { "c:/really/really/really/really/long/path",
! "../../../../../../../../.." },
! { "c:/really/really/really/really/long/path",
! "../../../../../../../../../" },
! { "c:/djgpp/share/doodah", "../foo" },
! { "c:/djgpp/share/doodah/", "../../foo" },
! { "c:/djgpp/share/doodah/", "../../../foo" },
! { "c:/djgpp/share/doodah", "../foo" },
! { "c:/djgpp/share/doodah/", "../../foo" },
! { "c:/djgpp/share/doodah/", "../../../foo" },
! { NULL, NULL }
! };
!
! const char *solve_tests[] = {
! /* These aren't symlinks. */
! "c:../../../../../../../..",
! "c:../..",
! "/dev/env/DJDIR",
! "/dev/env/DJDIR/djgpp.env",
! "/dev/env/DJDIR/../../..",
! NULL
! };
!
! const char *gcac_tests[] = {
! NULL
! };
!
! char buf[FILENAME_MAX], buf2[FILENAME_MAX];;
! int i;
!
! puts("collapse_dirs_and_dotdots tests:");
!
! for (i = 0; cddd_tests[i].prefix != NULL; i++) {
! strcpy(buf, cddd_tests[i].prefix);
! strcpy(buf2, cddd_tests[i].suffix);
!
! printf("[%s, %s] -> ", buf, buf2);
! collapse_dirs_and_dotdots(buf, buf2);
! printf("[%s, %s]\n", buf, buf2);
! }
!
! puts("\n__solve_symlinks tests:");
!
! for (i = 0; solve_tests[i] != NULL; i++) {
! memset(buf, 0, sizeof(buf));
! __solve_symlinks(solve_tests[i], buf);
! printf("%s -> %s\n", solve_tests[i], buf);
! }
!
! puts("\nget_cwd_and_collapse tests:");
!
! for (i = 0; cddd_tests[i].prefix != NULL; i++) {
! strcpy(buf, cddd_tests[i].suffix);
! memset(buf2, 0, sizeof(buf2));
! get_cwd_and_collapse(buf, buf2);
! printf("%s -> %s\n", buf, buf2);
! }
!
! for (i = 0; solve_tests[i] != NULL; i++) {
! strcpy(buf, solve_tests[i]);
! memset(buf2, 0, sizeof(buf2));
! get_cwd_and_collapse(buf, buf2);
! printf("%s -> %s\n", buf, buf2);
! }
!
! for (i = 0; gcac_tests[i] != NULL; i++) {
! strcpy(buf, gcac_tests[i]);
! memset(buf2, 0, sizeof(buf2));
! get_cwd_and_collapse(buf, buf2);
! printf("%s -> %s\n", buf, buf2);
! }
!
! puts("User requested tests:");
! for (i = 1; i < argc; i++) {
! memset(buf, 0, sizeof(buf));
! __solve_symlinks(argv[i], buf);
! printf("%s -> %s\n", argv[i], buf);
! }
!
! return EXIT_SUCCESS;
}
+ #endif
Index: src/libc/compat/unistd/xsymlink.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/xsymlink.txh,v
retrieving revision 1.1
diff -p -c -3 -r1.1 xsymlink.txh
*** src/libc/compat/unistd/xsymlink.txh 14 Aug 2000 08:51:30 -0000 1.1
--- src/libc/compat/unistd/xsymlink.txh 16 Jan 2003 09:53:12 -0000
*************** int __solve_symlinks(const char *symlink
*** 8,19 ****
--- 8,23 ----
@end example
@subheading Description
+
This function fully resolves given symlink in @var{symlink_path}---all
path components and all symlink levels are resolved. The
returned path in @var{real_path} is guaranteed to be symlink-clean
and understandable by DOS. If @var{symlink_path} does not contain
symlinks at all, it is simply copied to @var{real_path}.
+ @var{real_path} should be of size @code{FILENAME_MAX}, to contain
+ the maximum-length path.
+
@subheading Return Value
Zero in case of error (and @code{errno} set to the appropriate
*************** error code), non-zero in case of success
*** 30,38 ****
#include <libc/symlink.h>
#include <stdio.h>
__solve_symlinks(fn, file_name);
printf("File %s is really %s\n", fn, file_name);
-
@end example
-
--- 34,43 ----
#include <libc/symlink.h>
#include <stdio.h>
+ const char fn[] = "c:/somedir/somelink";
+ char file_name[FILENAME_MAX];
+
__solve_symlinks(fn, file_name);
printf("File %s is really %s\n", fn, file_name);
@end example
Index: src/libc/compat/unistd/sdirlink.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/sdirlink.txh,v
retrieving revision 1.1
diff -p -c -3 -r1.1 sdirlink.txh
*** src/libc/compat/unistd/sdirlink.txh 25 Aug 2000 11:35:37 -0000 1.1
--- src/libc/compat/unistd/sdirlink.txh 16 Jan 2003 09:53:23 -0000
*************** int __solve_dir_symlinks(const char *sym
*** 8,18 ****
--- 8,22 ----
@end example
@subheading Description
+
This function resolves given symlink in @var{symlink_path}---all path
components @strong{except} the last one and all symlink levels are
resolved. If @var{symlink_path} does not contain symlinks at all, it is
simply copied to @var{real_path}.
+ @var{real_path} should be of size @code{FILENAME_MAX}, to contain
+ the maximum-length path.
+
@subheading Return Value
Zero in case of error (and @code{errno} set to the appropriate
*************** error code), non-zero in case of success
*** 29,37 ****
#include <libc/symlink.h>
#include <stdio.h>
__solve_dir_symlinks(fn, file_name);
printf("The real path to %s is %s\n", fn, file_name);
-
@end example
-
--- 33,42 ----
#include <libc/symlink.h>
#include <stdio.h>
+ char fn[] = "c:/somelink/someotherlink/somefile";
+ char file_name[FILENAME_MAX];
+
__solve_dir_symlinks(fn, file_name);
printf("The real path to %s is %s\n", fn, file_name);
@end example
Index: include/sys/stat.h
===================================================================
RCS file: /cvs/djgpp/djgpp/include/sys/stat.h,v
retrieving revision 1.7
diff -p -c -3 -r1.7 stat.h
*** include/sys/stat.h 17 Oct 2002 23:00:24 -0000 1.7
--- include/sys/stat.h 16 Jan 2003 09:53:24 -0000
*************** mode_t umask(mode_t _cmask);
*** 84,93 ****
--- 84,95 ----
void _fixpath(const char *, char *);
char * __canonicalize_path(const char *, char *, size_t);
+ char * __get_current_directory(char *out, int drive_number);
unsigned short _get_magic(const char *, int);
int _is_executable(const char *, int, const char *);
int lstat(const char * _path, struct stat * _buf);
int mknod(const char *_path, mode_t _mode, dev_t _dev);
+ char * __partially_canonicalize_path(const char *, char *, size_t);
char * _truename(const char *, char *);
/* Bit-mapped variable _djstat_flags describes what expensive
Index: src/libc/posix/sys/stat/fixpath.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/fixpath.c,v
retrieving revision 1.8
diff -p -c -3 -r1.8 fixpath.c
*** src/libc/posix/sys/stat/fixpath.c 25 Sep 2002 21:45:20 -0000 1.8
--- src/libc/posix/sys/stat/fixpath.c 16 Jan 2003 09:53:44 -0000
***************
*** 1,3 ****
--- 1,4 ----
+ /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
***************
*** 5,10 ****
--- 6,12 ----
/* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
#include <libc/stubs.h>
+ #include <libc/unconst.h>
#include <stdio.h> /* For FILENAME_MAX */
#include <stdlib.h>
#include <errno.h> /* For errno */
***************
*** 19,27 ****
static unsigned use_lfn;
! static char *__get_current_directory(char *out, int drive_number);
!
! static char *
__get_current_directory(char *out, int drive_number)
{
__dpmi_regs r;
--- 21,27 ----
static unsigned use_lfn;
! char *
__get_current_directory(char *out, int drive_number)
{
__dpmi_regs r;
*************** is_term(int c)
*** 130,140 ****
*/
char *
! __canonicalize_path(const char *in, char *out, size_t path_max)
{
int drive_number;
! char in1[FILENAME_MAX];
! char *ip;
char *op = out;
int preserve_case = _preserve_fncase();
char *name_start;
--- 130,139 ----
*/
char *
! __partially_canonicalize_path(const char *in, char *out, size_t path_max)
{
int drive_number;
! char *ip = unconst(in, char *);
char *op = out;
int preserve_case = _preserve_fncase();
char *name_start;
*************** __canonicalize_path(const char *in, char
*** 148,158 ****
op_limit = op + path_max;
- /* Perform the same magic conversions that _put_path does. */
- _put_path(in);
- dosmemget(__tb, sizeof(in1), in1);
- ip = in1;
-
/* Add drive specification to output string */
if (((*ip >= 'a' && *ip <= 'z') ||
(*ip >= 'A' && *ip <= 'Z'))
--- 147,152 ----
*************** __canonicalize_path(const char *in, char
*** 318,323 ****
--- 312,329 ----
}
return out;
+ }
+
+ char *
+ __canonicalize_path(const char *in, char *out, size_t path_max)
+ {
+ char in1[FILENAME_MAX];
+
+ /* Perform the same magic conversions that _put_path does. */
+ _put_path(in);
+ dosmemget(__tb, sizeof(in1), in1);
+
+ return __partially_canonicalize_path(in1, out, path_max);
}
void
Index: src/libc/compat/unistd/sdirlink.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/sdirlink.c,v
retrieving revision 1.5
diff -p -c -3 -r1.5 sdirlink.c
*** src/libc/compat/unistd/sdirlink.c 17 Oct 2002 23:00:25 -0000 1.5
--- src/libc/compat/unistd/sdirlink.c 16 Jan 2003 09:53:44 -0000
*************** int __solve_dir_symlinks(const char * __
*** 72,74 ****
--- 72,93 ----
strcat(__real_path, last_part);
return 1;
}
+
+ #ifdef TEST
+
+ #include <stdlib.h>
+
+ int main(int argc, char *argv[])
+ {
+ char buf[FILENAME_MAX];
+ int i;
+
+ puts("User requested tests:");
+ for (i = 1; i < argc; i++) {
+ __solve_dir_symlinks(argv[i], buf);
+ printf("%s -> %s\n", argv[i], buf);
+ }
+
+ return EXIT_SUCCESS;
+ }
+ #endif
- Raw text -