Mail Archives: djgpp-workers/2000/08/08/05:40:06
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 -