Message-ID: <39998D7D.70F85B9E@softhome.net> Date: Tue, 15 Aug 2000 20:35:41 +0200 From: Laurynas Biveinis X-Mailer: Mozilla 4.74 [en] (Win98; U) X-Accept-Language: lt,en MIME-Version: 1.0 To: DJGPP Workers Subject: Patch: open() adjustment for symlinks Content-Type: text/plain; charset=iso-8859-4 Content-Transfer-Encoding: 7bit Reply-To: djgpp-workers AT delorie DOT com It makes open() recognize symlinks, adds two new mode flags for it (O_NOLINK and O_NOFOLLOW), updates docs and adds testsuite. Any comments? Laurynas Index: djgpp/include/fcntl.h =================================================================== RCS file: /cvs/djgpp/djgpp/include/fcntl.h,v retrieving revision 1.3 diff -u -r1.3 fcntl.h --- fcntl.h 1998/06/28 22:27:10 1.3 +++ fcntl.h 2000/08/15 18:32:41 @@ -60,6 +60,11 @@ #ifndef _POSIX_SOURCE +/* Additional non-POSIX flags for open(). */ +/* They are present on GNU libc. */ +#define O_NOLINK 0x4000 +#define O_NOFOLLOW 0x8000 + #define SH_COMPAT 0x0000 #define SH_DENYRW 0x0010 #define SH_DENYWR 0x0020 Index: djgpp/src/docs/kb/wc204.txi =================================================================== RCS file: /cvs/djgpp/djgpp/src/docs/kb/wc204.txi,v retrieving revision 1.19 diff -u -r1.19 wc204.txi --- wc204.txi 2000/08/15 17:38:40 1.19 +++ wc204.txi 2000/08/15 18:32:42 @@ -102,6 +102,10 @@ new functions @code{__solve_symlinks}, @code{lstat} and @code{readlink}; new macros @code{S_ISLNK} and @code{S_IFLNK} have been added to library. +@findex open AT r{, changed behaviour} +@code{open} now follows symlinks when opening file. Also, it honors two new +mode flags: @code{O_NOLINK} and @code{O_NOFOLLOW}. + @findex symlink AT r{, changed behaviour} As a part of symlink emulation, @code{symlink} no longer emulates symlinks to executables by creating stubs. It creates symlinks to all files instead. Index: djgpp/src/libc/posix/fcntl/open.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/fcntl/open.c,v retrieving revision 1.5 diff -u -r1.5 open.c --- open.c 1999/06/03 17:27:37 1.5 +++ open.c 2000/08/15 18:32:52 @@ -1,9 +1,13 @@ +/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include +#include +#include +#include #include #include #include @@ -18,15 +22,62 @@ /* Extra share flags that can be indicated by the user */ int __djgpp_share_flags; +/* Helper function for O_NOFOLLOW handling. */ +static char *find_last_sep(const char * str); + int open(const char* filename, int oflag, ...) { int fd, dmode, bintext, dont_have_share; + char real_name[FILENAME_MAX + 1]; int should_create = (oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL); + /* Solve symlinks and honor O_NOLINK flag */ + if (oflag & O_NOLINK) + strcpy(real_name, filename); + else + { + if (!__solve_symlinks(filename, real_name)) + return -1; /* errno from from __solve_symlinks() */ + } + + /* Honor O_NOFOLLOW flag. */ + if (oflag & O_NOFOLLOW) + { + /* O_NOFOLLOW, as defined in glibc, requires open() to fail if the + * last path component is a symlink. However, it still requires to + * resolve all other path components. + * We check if there were any symlinks by comparing __solve_symlinks() + * input and output. That function does not perform any path + * canonicalization so it should be safe. */ + if (strcmp(filename, real_name)) + { + /* Yes, there were symlinks in the path. Now take all but the last + * path component from `real_name', add last path component from + * `filename', and try to resolve that mess. + */ + char temp[FILENAME_MAX + 1]; + char resolved[2]; + char * last_separator; + int old_errno = errno; + strcpy(temp, real_name); + last_separator = find_last_sep(temp); + *last_separator = '\0'; + last_separator = find_last_sep(filename); + strcat(temp, last_separator); + if ((readlink(temp, resolved, 1) != -1) || (errno != EINVAL)) + { + /* Yes, the last path component was a symlink. */ + errno = ELOOP; + return -1; + } + errno = old_errno; + } + } + /* Check this up front, to reduce cost and minimize effect */ if (should_create) - if (__file_exists(filename)) + if (__file_exists(real_name)) { /* file exists and we didn't want it to */ errno = EEXIST; @@ -55,10 +106,10 @@ } if (should_create) - fd = _creatnew(filename, dmode, oflag & 0xff); + fd = _creatnew(real_name, dmode, oflag & 0xff); else { - fd = _open(filename, oflag); + fd = _open(real_name, oflag); if (fd == -1) { @@ -67,7 +118,7 @@ if (errno == EMFILE || errno == ENFILE) return fd; - if (__file_exists(filename)) + if (__file_exists(real_name)) { /* Under multi-taskers, such as Windows, our file might be open by some other program with DENY-NONE sharing bit, @@ -75,12 +126,12 @@ DENY-NONE bit set, unless some sharing bits were already set in the initial call. */ if (dont_have_share) - fd = _open(filename, oflag | SH_DENYNO); + fd = _open(real_name, oflag | SH_DENYNO); } /* Don't call _creat on existing files for which _open fails, since the file could be truncated as a result. */ else if ((oflag & O_CREAT)) - fd = _creat(filename, dmode); + fd = _creat(real_name, dmode); } } @@ -101,4 +152,14 @@ lseek(fd, 0, SEEK_END); return fd; +} + +static char *find_last_sep(const char * str) +{ + char * res = strrchr(str, '/'); + if (!res) + res = strrchr(str, '\\'); + if (!res) + res = unconst(str, char*); + return res; } Index: djgpp/src/libc/posix/fcntl/open.txh =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/fcntl/open.txh,v retrieving revision 1.3 diff -u -r1.3 open.txh --- open.txh 1998/09/27 15:22:12 1.3 +++ open.txh 2000/08/15 18:32:52 @@ -62,6 +62,16 @@ function (@pxref{__djgpp_set_ctrl_c}) if you want @kbd{Ctrl-C} to generate interrupts while console is read in binary mode. +@item O_NOFOLLOW + +@code{open} will fail with errno set to ELOOP, if the last patch component +in @var{file} is symlink. + +@item O_NOLINK + +If @var{file} is a symlink, @code{open} will open symlink file itself instead +of referred file. + @end table If the file is created by this call, it will be given the read/write Index: djgpp/tests/libc/posix/fcntl/file1 =================================================================== RCS file: file1 diff -N file1 --- /dev/null Tue May 5 16:32:27 1998 +++ file1 Tue Aug 15 14:32:58 2000 @@ -0,0 +1 @@ +file1 Index: djgpp/tests/libc/posix/fcntl/makefile =================================================================== RCS file: /cvs/djgpp/djgpp/tests/libc/posix/fcntl/makefile,v retrieving revision 1.1 diff -u -r1.1 makefile --- makefile 1995/08/27 07:25:44 1.1 +++ makefile 2000/08/15 18:32:58 @@ -2,6 +2,7 @@ SRC += binpr.c SRC += bt.c +SRC += open.c SRC += trunc.c include $(TOP)/../makefile.inc Index: djgpp/tests/libc/posix/fcntl/open.c =================================================================== RCS file: open.c diff -N open.c --- /dev/null Tue May 5 16:32:27 1998 +++ open.c Tue Aug 15 14:32:58 2000 @@ -0,0 +1,89 @@ +/* Testsuite for open() + * TODO: only symlink handling aspect is checked. Other things should be + * checked too. + * Currently there are following tests: + * 1. Open a symlink. Check if we really have opened a referred file. + * 2. Open a symlink with O_NOLINK flag. Check if we really have opened a + * symlink file itself. + * 3. Open simple file in a symlink subdir with O_NOFOLLOW flag. Check if + * we really have opened a referred file. + * 4. Open symlink in a symlink subdir with O_NOFOLLOW flag. Should fail with ELOOP. + * ELOOP. + */ +#include +#include +#include +#include +#include +#include + +static void test_success(int testnum, const char * fn, int flags, + int data_size, const char * data); + +int main(void) +{ + int fd; + if (!__file_exists("file1") || !__file_exists("test1") || + !__file_exists("test2") || !__file_exists("dir/file1")) + { + fprintf(stderr, "Required data file is missing\n"); + exit(1); + } + test_success(1, "test1", O_RDONLY, 5, "file1"); + test_success(2, "test1", O_RDONLY | O_NOLINK, 10, "!"); + test_success(3, "test2/file1", O_RDONLY | O_NOFOLLOW, 5, "file1"); + fd = open("test2/test1", O_RDONLY | O_NOFOLLOW); + if (fd != -1) + { + fprintf(stderr, "Test 4 failed - unexpected open() success.\n"); + exit(1); + } + if (errno != ELOOP) + { + perror("Test 4 failed - wrong errno returned "); + exit(1); + } + printf("Test 4 passed\n"); + return 0; +} + +static void test_success(int testnum, const char * fn, int flags, + int data_size, const char * data) +{ + char err_buf[50]; + int bytes_read; + char buffer[100]; + int fd = open(fn, flags); + if (fd == -1) + { + sprintf(err_buf, "Test %d failed - unexpected open() failure ", testnum); + perror(err_buf); + exit(1); + } + bytes_read = read(fd, buffer, data_size); + if (bytes_read == -1) + { + sprintf(err_buf, "Test %d failed - unexpected read() failure ", testnum); + perror(err_buf); + close(fd); + exit(1); + } + if (bytes_read != data_size) + { + fprintf(stderr, + "Test %d failed - read() returned less bytes than expected.\n", testnum); + buffer[bytes_read] = '\0'; + printf("buffer = %s\n", buffer); + close(fd); + exit(1); + } + if (strncmp(buffer, data, data_size)) + { + fprintf(stderr, "Test %d failed - read() returned wrong file data.\n", + testnum); + close(fd); + exit(1); + } + close(fd); + printf("Test %d passed\n", testnum); +} Index: djgpp/tests/libc/posix/fcntl/test1 =================================================================== RCS file: test1 diff -N test1 --- /dev/null Tue May 5 16:32:27 1998 +++ test1 Tue Aug 15 14:32:58 2000 @@ -0,0 +1,12 @@ +!file1 + + + + + + + + + + + \ No newline at end of file Index: djgpp/tests/libc/posix/fcntl/test2 =================================================================== RCS file: test2 diff -N test2 --- /dev/null Tue May 5 16:32:27 1998 +++ test2 Tue Aug 15 14:32:59 2000 @@ -0,0 +1,12 @@ +!dir + + + + + + + + + + + \ No newline at end of file Index: djgpp/tests/libc/posix/fcntl/dir/file1 =================================================================== RCS file: file1 diff -N file1 --- /dev/null Tue May 5 16:32:27 1998 +++ file1 Tue Aug 15 14:32:59 2000 @@ -0,0 +1 @@ +file1 Index: djgpp/tests/libc/posix/fcntl/dir/test1 =================================================================== RCS file: test1 diff -N test1 --- /dev/null Tue May 5 16:32:27 1998 +++ test1 Tue Aug 15 14:32:59 2000 @@ -0,0 +1,12 @@ +!file1 + + + + + + + + + + + \ No newline at end of file