delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2000/08/11/08:26:24

Message-ID: <3993F0E7.9A25BD50@softhome.net>
Date: Fri, 11 Aug 2000 14:26:15 +0200
From: Laurynas Biveinis <lauras AT softhome DOT net>
X-Mailer: Mozilla 4.74 [en] (Win98; U)
X-Accept-Language: lt,en
MIME-Version: 1.0
To: DJGPP Workers <djgpp-workers AT delorie DOT com>
Subject: Patch: __solve_symlinks()
Reply-To: djgpp-workers AT delorie DOT com

This new function is the core of symlink support - it will be
called by most file handling functions to resolve symlinks
before calling DOS. If it breaks, everything else breaks too.

So please report any bugs there ASAP. The first one who 
shows me a testcase where __solve_symlinks does not correctly
work, will receive my thanks via snail-mail postcard ;-)

Laurynas

? djgpp/djgpp.diff
? djgpp/src/libc/posix/sys/stat.diff
Index: djgpp/include/libc/symlink.h
===================================================================
RCS file: symlink.h
diff -N symlink.h
--- /dev/null	Tue May  5 16:32:27 1998
+++ symlink.h	Fri Aug 11 08:14:51 2000
@@ -0,0 +1,39 @@
+/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
+
+/* Written by Laurynas Biveinis                                */
+/* This file contains some internal info related to symlinks   */
+/* Note: symlink file format is still in internal include file */
+/* because I don't think it's required for user apps           */
+#ifndef __dj_include_libc_symlink_h_
+#define __dj_include_libc_symlink_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __dj_ENFORCE_ANSI_FREESTANDING
+
+#ifndef __STRICT_ANSI__
+
+#ifndef _POSIX_SOURCE
+
+/* A prototype for internal library function for fully resolving symlink   */
+/* chain. Standard function readlink() solves only one symlink level.      */
+/* If path name passed appears to be not a symlink, it is copied to result */
+/* string. Return code 1 means success, 0 - failure (to many links - errno */
+/* is set too).                                                            */
+
+int __solve_symlinks(const char * __symlink_path, char * __real_path);
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !__STRICT_ANSI__ */
+#endif /* !__dj_ENFORCE_ANSI_FREESTANDING */
+
+#ifndef __dj_ENFORCE_FUNCTION_CALLS
+#endif /* !__dj_ENFORCE_FUNCTION_CALLS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__dj_include_libc__h_ */
Index: djgpp/src/libc/compat/unistd/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/makefile,v
retrieving revision 1.5
diff -u -r1.5 makefile
--- makefile	2000/08/11 11:16:23	1.5
+++ makefile	2000/08/11 12:14:56
@@ -18,5 +18,6 @@
 SRC += truncate.c
 SRC += usleep.c
 SRC += vfork.c
+SRC += xsymlink.c
 
 include $(TOP)/../makefile.inc
Index: djgpp/src/libc/compat/unistd/xsymlink.c
===================================================================
RCS file: xsymlink.c
diff -N xsymlink.c
--- /dev/null	Tue May  5 16:32:27 1998
+++ xsymlink.c	Fri Aug 11 08:14:56 2000
@@ -0,0 +1,156 @@
+/* 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"
+
+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)
+   {
+      /* 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 = readlink(resolved, 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 <= _POSIX_LINK_MAX));
+      if (link_level > _POSIX_LINK_MAX)
+      {
+         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 */
+          start = end;
+          if ((*start == '/') || (*start == '\\'))
+             ++start;
+          end = strpbrk(end + 1, "/\\");
+      }
+      if (!end)
+          end = __real_path + strlen(__real_path);
+   }
+   return 1;
+}
+
Index: djgpp/src/libc/compat/unistd/xsymlink.txh
===================================================================
RCS file: xsymlink.txh
diff -N xsymlink.txh
--- /dev/null	Tue May  5 16:32:27 1998
+++ xsymlink.txh	Fri Aug 11 08:14:56 2000
@@ -0,0 +1,37 @@
+@node __solve_symlinks, io
+@subheading Syntax
+
+@example
+#include <libc/symlink.h>
+
+int __solve_symlinks(const char *symlink_path, char *real_path);
+@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}.
+@subheading Return Value
+
+Zero in case of error (and @code{errno} set to the appropriate
+error code), non-zero in case of success.
+
+@subheading Portability
+
+@portability !ansi, !posix
+
+@subheading Example
+
+@example
+
+  #include <libc/symlink.h>
+  #include <stdio.h>
+
+  __solve_symlinks(fn, file_name);
+  printf("File %s is really %s\n", fn, file_name);
+
+
+@end example
+
Index: djgpp/tests/libc/compat/unistd/fail1
===================================================================
RCS file: fail1
diff -N fail1
--- /dev/null	Tue May  5 16:32:27 1998
+++ fail1	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>fail2
+                                                                    
+                                             
+                                                   
+                                                 
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/fail2
===================================================================
RCS file: fail2
diff -N fail2
--- /dev/null	Tue May  5 16:32:27 1998
+++ fail2	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>fail1
+                                                                    
+                                             
+                                                   
+                                                 
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/fail3
===================================================================
RCS file: fail3
diff -N fail3
--- /dev/null	Tue May  5 16:32:27 1998
+++ fail3	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>dir1/fail1
+                                                                  
+                                           
+                                                  
+                                                 
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/tests/libc/compat/unistd/makefile,v
retrieving revision 1.1
diff -u -r1.1 makefile
--- makefile	2000/08/11 11:16:25	1.1
+++ makefile	2000/08/11 12:15:11
@@ -1,5 +1,6 @@
 TOP=../..
 
 SRC += readlink.c
+SRC += xsymlink.c
 
 include $(TOP)/../makefile.inc
Index: djgpp/tests/libc/compat/unistd/test4
===================================================================
RCS file: test4
diff -N test4
--- /dev/null	Tue May  5 16:32:27 1998
+++ test4	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>test5
+                                                              
+                                                  
+                                                   
+                                                  
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/test5
===================================================================
RCS file: test5
diff -N test5
--- /dev/null	Tue May  5 16:32:27 1998
+++ test5	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>file2
+                                                              
+                                                  
+                                                   
+                                                  
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/test6
===================================================================
RCS file: test6
diff -N test6
--- /dev/null	Tue May  5 16:32:27 1998
+++ test6	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,13 @@
+!<symlink>dir1
+
+                                                              
+                                                  
+                                                   
+                                                  
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/xsymlink.c
===================================================================
RCS file: xsymlink.c
diff -N xsymlink.c
--- /dev/null	Tue May  5 16:32:27 1998
+++ xsymlink.c	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,105 @@
+/* Testsuite for __solve_symlinks()
+ * There are following success tests:
+ *   1. Simple case with symlink in current directory
+ *   2. Recursive symlinks
+ *   3. Symlink in subdirectory
+ *   4. Real file in symlinked directory
+ *   5. Symlink in symlinked directory
+ *   6. Symlink in symlinked directory to UNIX-style absolute path
+ *   7. The same with DOS-style absolute path
+ *   8. Symlink in symlinked directory to absolute path with symlinks itself
+ *   9. Real file in a symlink subdir in a symlink subdir
+ *  10. Symlink in a subdir to file in an upper dir
+ * Any unhandled cases are more than welcome.
+ *
+ * And following are failure tests:
+ *  11. Simple symlink loop.
+ *  12. Symlink loop across directories
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libc/symlink.h>
+#include <sys/stat.h>
+
+static void test_success(int num, const char * slink, const char * expect);
+static void test_failure(int num, const char * slink);
+
+int main(void)
+{
+   if (!__file_exists("test1") || !__file_exists("test4") ||
+       !__file_exists("test5") || !__file_exists("test6") ||
+       !__file_exists("fail1") || !__file_exists("fail2") ||
+       !__file_exists("fail3") || !__file_exists("dir1/fail1") ||
+       !__file_exists("dir1/test1") || !__file_exists("dir1/test2") ||
+       !__file_exists("dir1/test3") || !__file_exists("dir1/test4") ||
+       !__file_exists("dir1/test5") || !__file_exists("dir1/test6") ||
+       !__file_exists("dir1/test7") || access("dir1/dir2", D_OK))
+   {
+       fprintf(stderr, "Required data files not found");
+       exit(1);
+   }
+   printf("Running readlink() testsuite:\n");
+   test_success( 1, "test1", "file1");
+   test_success( 2, "test4", "file2");
+   test_success( 3, "dir1/test1", "dir1/file1");
+   test_success( 4, "test6/file1", "dir1/file1");
+   test_success( 5, "test6/test1", "dir1/file1");
+   test_success( 6, "test6/test2", "/dev/env/DJDIR/bin/gcc.exe");
+   test_success( 7, "test6/test3", "c:\\autoexec.bat");
+   test_success( 8, "test6/test4", "c:/dir/file");
+   test_success( 9, "test6/test6/file", "dir1/dir2/file");
+   /* Following one returns dir1/../file, which is perfectly valid but
+      forces us to use _fixpath before comparisson */
+   test_success(10, "dir1/test7", "file");
+   test_failure(11, "fail1");
+   test_failure(12, "fail3");
+   printf("Done.\n");
+   return 0;
+}
+
+static void test_success(int num, const char * slink, const char * expect)
+{
+   char real_name[FILENAME_MAX + 1];
+   char real_fixed[FILENAME_MAX + 1];
+   char expect_fixed[FILENAME_MAX + 1];
+   char err_buf[50];
+   if (!__solve_symlinks(slink, real_name))
+   {
+      sprintf(err_buf, "Test %d failed ", num);
+      perror(err_buf);
+      exit(1);
+   }
+   _fixpath(expect, expect_fixed);
+   _fixpath(real_name, real_fixed);
+   if (strcmp(real_fixed, expect_fixed))
+   {
+      fprintf(stderr,
+            "Test %d failed - __solve_symlinks returns wrong resolved path\n",
+            num);
+      exit(1);
+   }
+   printf("Test %d passed\n", num);
+}
+
+static void test_failure(int num, const char * slink)
+{
+   char buf[FILENAME_MAX + 1];
+   char err_buf[50];
+   errno = 0;
+   if (__solve_symlinks(slink, buf))
+   {
+      fprintf(stderr,
+            "Test %d failed - __solve_symlinks suceeds when it should fail\n",
+            num);
+      exit(1);
+   }
+   if (errno != ELOOP)
+   {
+      sprintf(err_buf, "Test %d failed - wrong errno returned ", num);
+      perror(err_buf);
+      exit(1);
+   }
+   printf("Test %d passed\n", num);
+}
Index: djgpp/tests/libc/compat/unistd/dir1/fail1
===================================================================
RCS file: fail1
diff -N fail1
--- /dev/null	Tue May  5 16:32:27 1998
+++ fail1	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>../fail3
+                                                                    
+                                            
+                                                  
+                                                
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/dir1/test1
===================================================================
RCS file: test1
diff -N test1
--- /dev/null	Tue May  5 16:32:27 1998
+++ test1	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>file1
+                                                              
+                                                  
+                                                   
+                                                  
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/dir1/test2
===================================================================
RCS file: test2
diff -N test2
--- /dev/null	Tue May  5 16:32:27 1998
+++ test2	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>/dev/env/DJDIR/bin/gcc.exe
+                                                         
+                                            
+                                              
+                                             
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/dir1/test3
===================================================================
RCS file: test3
diff -N test3
--- /dev/null	Tue May  5 16:32:27 1998
+++ test3	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>c:\autoexec.bat
+                                                                    
+                                            
+                                              
+                                             
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/dir1/test4
===================================================================
RCS file: test4
diff -N test4
--- /dev/null	Tue May  5 16:32:27 1998
+++ test4	Fri Aug 11 08:15:11 2000
@@ -0,0 +1,12 @@
+!<symlink>c:/Programs/test/test6/test5
+                                                             
+                                      
+                                              
+                                             
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/dir1/test5
===================================================================
RCS file: test5
diff -N test5
--- /dev/null	Tue May  5 16:32:27 1998
+++ test5	Fri Aug 11 08:15:12 2000
@@ -0,0 +1,12 @@
+!<symlink>c:/dir/file
+                                                                  
+                                          
+                                                  
+                                                 
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/dir1/test6
===================================================================
RCS file: test6
diff -N test6
--- /dev/null	Tue May  5 16:32:27 1998
+++ test6	Fri Aug 11 08:15:12 2000
@@ -0,0 +1,12 @@
+!<symlink>dir2
+                                                                     
+                                             
+                                                   
+                                                 
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file
Index: djgpp/tests/libc/compat/unistd/dir1/test7
===================================================================
RCS file: test7
diff -N test7
--- /dev/null	Tue May  5 16:32:27 1998
+++ test7	Fri Aug 11 08:15:12 2000
@@ -0,0 +1,12 @@
+!<symlink>../file
+                                                                    
+                                            
+                                                  
+                                                 
+                                                   
+                                                 
+                                                  
+                                                  
+                                                   
+         
+           
\ No newline at end of file

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019