delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2000/08/08/05:40:06

Message-ID: <398FD55D.31201350@softhome.net>
Date: Tue, 08 Aug 2000 11:39:41 +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: readlink
Reply-To: djgpp-workers AT delorie DOT com

Here is the first part of actual symlink support. It adds readlink(),
its docs and testsuite. For testsuite I've created new tests/libc/compat/unistd
directory. And testsuite is A Good Thing (tm) indeed, it helped me to find ~3 bugs
in readlink() :)

Laurynas

Index: djgpp/include/unistd.h
===================================================================
RCS file: /cvs/djgpp/djgpp/include/unistd.h,v
retrieving revision 1.7
diff -u -r1.7 unistd.h
--- unistd.h	2000/07/26 15:17:42	1.7
+++ unistd.h	2000/08/08 09:32:55
@@ -138,6 +138,7 @@
 int             lchown(const char * file, int owner, int group);
 offset_t	llseek(int _fildes, offset_t _offset, int _whence);
 int		nice(int _increment);
+int             readlink(const char * __file, char * __buffer, size_t __size);
 void *		sbrk(int _delta);
 int		symlink (const char *, const char *);
 int		sync(void);
Index: djgpp/src/docs/kb/wc204.txi
===================================================================
RCS file: /cvs/djgpp/djgpp/src/docs/kb/wc204.txi,v
retrieving revision 1.16
diff -u -r1.16 wc204.txi
--- wc204.txi	2000/08/05 16:52:36	1.16
+++ wc204.txi	2000/08/08 09:32:56
@@ -94,3 +94,7 @@
 @code{struct itimerval} set to zero no longer causes the timer to behave
 as if @code{it_interval.tv_usec} were set to the system clock
 granularity (55 AT dmn{msec} by default).
+
+@findex readlink AT r{, and symlink support}
+UNIX-style symbolic links are fully emulated by library. As a part of this,
+new function @code{readlink} has been added to library.
Index: djgpp/src/libc/compat/unistd/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/makefile,v
retrieving revision 1.4
diff -u -r1.4 makefile
--- makefile	2000/07/26 15:17:47	1.4
+++ makefile	2000/08/08 09:33:00
@@ -13,6 +13,7 @@
 SRC += lchown.c
 SRC += llseek.c
 SRC += nice.c
+SRC += readlink.c
 SRC += sync.c
 SRC += truncate.c
 SRC += usleep.c
Index: djgpp/src/libc/compat/unistd/readlink.c
===================================================================
RCS file: readlink.c
diff -N readlink.c
--- /dev/null	Tue May  5 16:32:27 1998
+++ readlink.c	Tue Aug  8 05:33:00 2000
@@ -0,0 +1,69 @@
+/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
+
+#include <libc/stubs.h>
+#include <dir.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "xsymlink.h"
+
+int readlink(const char * filename, char * buffer, size_t size)
+{
+   ssize_t       bytes_read         = 0;
+   char          buf[_SYMLINK_FILE_LEN];
+   char        * data_buf;
+   int           fd;
+   struct ffblk  file_info;
+
+   /* Reject NULLs */
+   if (!filename || !buffer)
+   {
+      errno = EINVAL;
+      return -1;
+   }
+
+   /* Does symlink file exist at all? */
+   if (findfirst(filename, &file_info, 0))
+   {
+      errno = ENOENT;
+      return -1;
+   }
+
+   /* Is symlink file size a fixed magic value? */
+   if (file_info.ff_fsize != _SYMLINK_FILE_LEN)
+   {
+      errno = EINVAL; /* Sad but true */
+      return -1;
+   }
+
+   /* Now we check for special DJGPP symlink format */
+   fd = _open(filename, O_RDONLY);
+   if (fd < 0)
+      return -1; /* errno from open() call */
+
+   bytes_read = read(fd, &buf, _SYMLINK_FILE_LEN);
+   _close(fd);
+   if (bytes_read == -1)
+      return -1; /* Return errno set by _read() (_close() in worse case) */
+      
+   /* Check for symlink file header */
+   if (strncmp(buf, _SYMLINK_PREFIX, _SYMLINK_PREFIX_LEN))
+   {
+      close(fd);
+      errno = EINVAL;
+      return -1;
+   }
+   
+   data_buf = buf + _SYMLINK_PREFIX_LEN;
+   bytes_read = strchr(data_buf, '\n') - data_buf;
+   bytes_read = ((unsigned)bytes_read > size) ? size : bytes_read;
+   memcpy(buffer, data_buf, bytes_read);
+   return bytes_read;
+}
+
Index: djgpp/src/libc/compat/unistd/readlink.txh
===================================================================
RCS file: readlink.txh
diff -N readlink.txh
--- /dev/null	Tue May  5 16:32:27 1998
+++ readlink.txh	Tue Aug  8 05:33:00 2000
@@ -0,0 +1,38 @@
+@node readlink, io
+@subheading Syntax
+
+@example
+#include <unistd.h>
+
+int readlink(const char *filename, char *buffer, size_t size);
+@end example
+
+@subheading Description
+MSDOS doesn't support symbolic links but DJGPP emulates them.
+This function checks if @var{filename} is a DJGPP symlink and
+the file name that the links points to is copied into buffer,
+up to maximum @var{size} characters. Only the last file name
+is resolved.
+Portable applications should not assume that @var{buffer} is
+terminated with @code{'\0'}.
+
+@subheading Return Value
+
+Number of copied characters; value -1 is returned in case of
+error and @code{errno} is set. When value returned is equal to 
+@var{size}, you cannot determine if there was enough room to 
+copy whole name. So increase @var{size} and try again.
+
+@subheading Portability
+
+@portability !ansi, !posix
+
+@subheading Example
+
+@example
+char buf[FILENAME_MAX + 1];
+if (readlink("/dev/env/DJDIR/bin/sh.exe", buf, FILENAME_MAX) == -1)
+   if (errno == EINVAL)
+      puts("/dev/env/DJDIR/bin/sh.exe is not a symbolic link.");
+@end example
+
Index: djgpp/src/libc/compat/unistd/xsymlink.h
===================================================================
RCS file: xsymlink.h
diff -N xsymlink.h
--- /dev/null	Tue May  5 16:32:27 1998
+++ xsymlink.h	Tue Aug  8 05:33:00 2000
@@ -0,0 +1,19 @@
+/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
+/* Symlink support by Laurynas Biveinis                      */
+
+/* Internal header containing symlink file format specifiers     */
+/* I decided not to put it in public header, because this prefix */
+/* isn't guaranteed not to change.                               */
+#ifndef __XSYMLINK_H_
+#define __XSYMLINK_H_
+
+/* Current prefix is for being compatible with CygWin symlinks */
+#define _SYMLINK_PREFIX "!<symlink>"
+#define _SYMLINK_PREFIX_LEN (sizeof(_SYMLINK_PREFIX) - 1)
+
+/* Symlink files have fixed length - 510 bytes. Why this value? Why not? */
+/* It is big enough to hold longest possible path                        */
+#define _SYMLINK_FILE_LEN 510
+
+#endif /* #ifndef __XSYMLINK_H_ */
+
Index: djgpp/tests/libc/compat/unistd/makefile
===================================================================
RCS file: makefile
diff -N makefile
--- /dev/null	Tue May  5 16:32:27 1998
+++ makefile	Tue Aug  8 05:33:12 2000
@@ -0,0 +1,5 @@
+TOP=../..
+
+SRC += readlink.c
+
+include $(TOP)/../makefile.inc
Index: djgpp/tests/libc/compat/unistd/readlink.c
===================================================================
RCS file: readlink.c
diff -N readlink.c
--- /dev/null	Tue May  5 16:32:27 1998
+++ readlink.c	Tue Aug  8 05:33:12 2000
@@ -0,0 +1,121 @@
+/* Testsuite for readlink()
+ * There are following tests:
+ *   1. Simple case with valid symlink
+ *   2. Check if readlink writes beyond end of result buffer, if filename
+ * was longer
+ *   3 & 4. Check if it does not accept NULL args
+ *   5. Check with not present symlink file
+ *   6. Check with wrong size, OK contents symlink file
+ *   7. Check with OK size, wrong contents symlink file
+ */
+ 
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static void failure_test(int testno, const char * linkname, char * buf,
+                         int expect_errno,
+                         const char * errmsg1, const char * errmsg2);
+
+int main(void)
+{
+   int bytes_read;
+   char buffer[FILENAME_MAX + 1];
+   errno = 0;
+
+   /* Check if we have required files */
+   if (!__file_exists("test1") || !__file_exists("test2") ||
+       !__file_exists("test3"))
+   {
+      fprintf(stderr, "Cannot run testsuite - required files missing");
+      exit(1);
+   }
+   
+   /* Test 1 - simple case with symlink OK */
+   bytes_read = readlink("test1", buffer, FILENAME_MAX);
+   if (bytes_read == -1)
+   {
+      perror("Test 1 failed ");
+      exit(1);
+   }
+   /* Is buffer size OK? */
+   if (bytes_read != (signed)strlen("file1"))
+   {
+      fprintf(stderr, "Test 1 failed - readlink returns wrong buffer size\n");
+      exit(1);
+   }
+   /* Are buffer contents OK? */
+   if (strncmp(buffer, "file1", bytes_read))
+   {
+      fprintf(stderr, "Test 1 failed - readlink returns wrong link value\n");
+      exit(1);
+   }
+   printf("Test 1 passed\n");
+
+   /* Test 2 - check if readlink does not overwrite buffer */
+   memset(buffer, 0, sizeof(buffer));
+   buffer[4] = 0x7F;
+   bytes_read = readlink("test1", buffer, 4);
+   /* Is buffer size OK? */
+   if (bytes_read != 4)
+   {
+      fprintf(stderr, "Test 2 failed - readlink returns wrong buffer size\n");
+      exit(1);
+   }
+   /* Are buffer contents OK? */
+   if (strncmp(buffer, "file", bytes_read))
+   {
+      fprintf(stderr, "Test 2 failed - readlink returns wrong link value\n");
+      exit(1);
+   }
+   /* Does readlink have security hole? */
+   if (buffer[4] != 0x7F)
+   {
+      fprintf(stderr, "Test 2 failed - readlink writes beyond the buffer\n");
+      exit(1);
+   }
+   printf("Test 2 passed\n");
+
+   /* Tests 3 & 4 - stupid args */
+   failure_test(3, NULL, buffer, EINVAL, "readlink accepts NULL arg",
+                                 "readlink returns wrong errno for NULL arg");
+   failure_test(4, "doesntmatter", NULL, EINVAL, "readlink accepts NULL arg",
+                                 "readlink returns wrong errno for NULL arg");
+                                 
+   /* Test 5 - file not found */
+   failure_test(5, "/Pink/Floyd/Animals/Dogs/Shouldnt/Exist", buffer, ENOENT,
+                "readlink found non-existing file",
+                "readlink returns wrong errno for non-existing file");
+
+   /* Test 6: symlink file contents OK, size wrong */
+   failure_test(6, "test2", buffer, EINVAL,
+                "readlink accepted broken symlink file",
+                "readlink returns wrong errno for broken file");
+
+   /* Test 7: symlink file size wrong, contents OK */
+   failure_test(7, "test3", buffer, EINVAL,
+                "readlink accepted broken symlink file",
+                "readlink returns wrong errno for broken file");
+
+   return 0;
+}
+
+void failure_test(int testno, const char * linkname, char * buf,
+                  int expect_errno, const char * errmsg1, const char * errmsg2)
+{
+   int bytes_read;
+   errno = 0;
+   bytes_read = readlink(linkname, buf, FILENAME_MAX);
+   if (bytes_read != -1)
+   {
+      fprintf(stderr, "Test %d failed - %s\n", testno, errmsg1);
+      exit(1);
+   }
+   if (errno != expect_errno)
+   {
+      fprintf(stderr, "Test %d failed - %s\n", testno, errmsg2);
+      exit(1);
+   }
+   printf("Test %d passed\n", testno);
+}

- Raw text -


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