Message-ID: <385A69D1.F8B20F7A@softhome.net> Date: Fri, 17 Dec 1999 18:50:25 +0200 From: Laurynas Biveinis X-Mailer: Mozilla 4.7 [en] (Win98; I) X-Accept-Language: en MIME-Version: 1.0 To: DJGPP Workers Subject: The 5th symlink patch Content-Type: multipart/mixed; boundary="------------4C053DA2EC7A23114D04E754" Reply-To: djgpp-workers AT delorie DOT com This is a multi-part message in MIME format. --------------4C053DA2EC7A23114D04E754 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit There are two differences from the 4th patch: - I've found BSD manpage talking about symlinks, and there was mentioned another function which does not follow symlinks - lchown(). It was missing from my implementation. Now it isn't. (As well as its documentation). - After successful GCC bootstrap I tried to build binutils, but encountered the problem that C preprocessor still does not follow symlinks :( The fix was changing one digit to another. I've rebuilt lots of utilities with symlinks and no other problems encountered so far. Laurynas Biveinis ---------------------- --------------4C053DA2EC7A23114D04E754 Content-Type: text/plain; charset=us-ascii; name="symlink5.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="symlink5.diff" diff -u -r -N --minimal djgpp.old/include/fcntl.h djgpp/include/fcntl.h --- djgpp.old/include/fcntl.h Mon Jun 29 00:27:10 1998 +++ djgpp/include/fcntl.h Sat Dec 4 10:13:52 1999 @@ -1,3 +1,4 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #ifndef __dj_include_fcntl_h_ @@ -41,6 +42,7 @@ #define O_TRUNC 0x0800 #define O_APPEND 0x1000 #define O_NONBLOCK 0x2000 +#define O_NOLINK 0x4000 #include diff -u -r -N --minimal djgpp.old/include/libc/stubs.h djgpp/include/libc/stubs.h --- djgpp.old/include/libc/stubs.h Mon Sep 8 00:07:18 1997 +++ djgpp/include/libc/stubs.h Wed Dec 8 19:56:54 1999 @@ -49,6 +49,7 @@ #define pow10 __pow10 #define pow2 __pow2 #define putenv __putenv +#define readlink __readlink #define sbrk __sbrk #define setitimer __setitimer #define setmode __setmode diff -u -r -N --minimal djgpp.old/include/libc/symlink.h djgpp/include/libc/symlink.h --- djgpp.old/include/libc/symlink.h Thu Jan 1 00:00:00 1970 +++ djgpp/include/libc/symlink.h Sat Dec 4 09:52:38 1999 @@ -0,0 +1,39 @@ +/* Copyright (C) 1999 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_ */ diff -u -r -N --minimal djgpp.old/include/limits.h djgpp/include/limits.h --- djgpp.old/include/limits.h Wed Sep 9 20:14:52 1998 +++ djgpp/include/limits.h Sat Dec 4 10:14:00 1999 @@ -1,3 +1,4 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #ifndef __dj_include_limits_h_ @@ -34,7 +35,7 @@ #define _POSIX_ARG_MAX 16384 /* but only for exec's to other djgpp programs */ #define _POSIX_CHILD_MAX 7 /* limited by memory; 7 for 386MAX */ -#define _POSIX_LINK_MAX 1 /* POSIX says 8, but DOS says 1 */ +#define _POSIX_LINK_MAX 8 /* Symlink support allow this. */ #define _POSIX_MAX_CANON 126 /* POSIX says 255, but DOS says 126 */ #define _POSIX_MAX_INPUT 126 /* POSIX says 255, but DOS says 126 */ #define _POSIX_NAME_MAX 12 /* 8.3 */ diff -u -r -N --minimal djgpp.old/include/sys/stat.h djgpp/include/sys/stat.h --- djgpp.old/include/sys/stat.h Thu Aug 10 07:06:26 1995 +++ djgpp/include/sys/stat.h Sat Dec 4 10:14:28 1999 @@ -1,3 +1,4 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #ifndef __dj_include_sys_stat_h_ #define __dj_include_sys_stat_h_ @@ -15,6 +16,7 @@ #define S_ISDIR(m) (((m) & 0xf000) == 0x3000) #define S_ISFIFO(m) (((m) & 0xf000) == 0x4000) #define S_ISREG(m) (((m) & 0xf000) == 0x0000) +#define S_ISLNK(m) (((m) & 0xf000) == 0x8000) #define S_ISUID 0x80000000 #define S_ISGID 0x40000000 @@ -72,6 +74,7 @@ #define S_IFDIR 0x3000 #define S_IFIFO 0x4000 #define S_IFFIFO S_IFIFO +#define S_IFLNK 0x8000 #define S_IFLABEL 0x5000 #define S_ISLABEL(m) (((m) & 0xf000) == 0x5000) @@ -79,6 +82,7 @@ void _fixpath(const char *, char *); 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 * _truename(const char *, char *); diff -u -r -N --minimal djgpp.old/include/unistd.h djgpp/include/unistd.h --- djgpp.old/include/unistd.h Mon Jun 29 02:02:48 1998 +++ djgpp/include/unistd.h Fri Dec 17 18:34:16 1999 @@ -1,3 +1,4 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #ifndef __dj_include_unistd_h_ @@ -134,7 +135,9 @@ int gethostname(char *buf, int size); int getpagesize(void); char * getwd(char *__buffer); +int lchown(const char * file, int owner, int group); int nice(int _increment); +int readlink(const char * filename, char * buffer, size_t size); void * sbrk(int _delta); int symlink (const char *, const char *); int sync(void); diff -u -r -N --minimal djgpp.old/src/libc/ansi/errno/errno.txh djgpp/src/libc/ansi/errno/errno.txh --- djgpp.old/src/libc/ansi/errno/errno.txh Sat Mar 20 23:48:18 1999 +++ djgpp/src/libc/ansi/errno/errno.txh Sat Nov 6 19:54:12 1999 @@ -121,8 +121,9 @@ @item 18 -EMLINK -- Too many links. Not used in DJGPP (as DOS doesn't support -links). +EMLINK -- Too many links. Can be throwed by any library function working +with files or directories. Usually means encountered link loop (link1 -> link2, +link2 -> link1). @item 19 diff -u -r -N --minimal djgpp.old/src/libc/dos/dir/ftw.c djgpp/src/libc/dos/dir/ftw.c --- djgpp.old/src/libc/dos/dir/ftw.c Mon Aug 28 05:38:52 1995 +++ djgpp/src/libc/dos/dir/ftw.c Sat Dec 4 10:15:20 1999 @@ -1,3 +1,4 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ /* ftw() for DJGPP. * @@ -12,6 +13,7 @@ */ #include +#include #include #include #include @@ -107,6 +109,7 @@ { int flag; unsigned char pathbuf[FILENAME_MAX]; + unsigned char real_path[FILENAME_MAX]; int dirattr; int len; int e = errno; @@ -135,7 +138,12 @@ /* Fail for non-directories. */ errno = 0; - dirattr = _chmod(pathbuf, 0, 0); + /* _chmod() does not recognize symlinks, */ + /* so we give real name instead. */ + if (!_solve_symlinks(pathbuf, real_path)) + return -1; + + dirattr = _chmod(real_path, 0, 0); if (errno == ENOENT) return -1; else if ((dirattr & 0x10) != 0x10) diff -u -r -N --minimal djgpp.old/src/libc/dos/process/dosexec.c djgpp/src/libc/dos/process/dosexec.c --- djgpp.old/src/libc/dos/process/dosexec.c Thu Jun 3 19:27:36 1999 +++ djgpp/src/libc/dos/process/dosexec.c Sun Nov 7 16:52:50 1999 @@ -3,6 +3,7 @@ /* 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 @@ -518,6 +519,7 @@ int i; char *go32, *sip=0; char rpath[FILENAME_MAX]; + char real_program[FILENAME_MAX]; int argc=0; int si_la=0, si_off=0, rm_off, argv_off; @@ -526,6 +528,11 @@ int retval; int lfn = 2; /* means don't know yet */ + /* LB: resolve symlinks */ + if (!_solve_symlinks(program, real_program)) + return -1; + + type = _check_v2_prog (program, -1); /* Because this function is called only, when program @@ -545,7 +552,7 @@ if (!is_coff) { if (type->exec_format == _V2_EXEC_FORMAT_EXE) - return direct_exec(program, argv, envp); + return direct_exec(real_program, argv, envp); else return __dosexec_command_exec (program, argv, envp); } @@ -559,7 +566,7 @@ if (v2_0 && is_stubbed) { - strcpy(rpath, program); + strcpy(rpath, real_program); } else { @@ -567,7 +574,7 @@ if (!__dosexec_find_on_path(go32, envp, rpath)) { errno = e; - return direct_exec(program, argv, envp); /* give up and just run it */ + return direct_exec(real_program, argv, envp); /* give up and just run it */ } if (found_si) @@ -585,7 +592,7 @@ usual DOS command line and the !proxy one (which will be put into the environment). Sigh... */ save_argv0 = argv[0]; - argv[0] = unconst(program, char *); /* since that's where we really found it */ + argv[0] = unconst(program, char *); /* Construct the DOS command tail */ for (argc=0; argv[argc]; argc++); @@ -613,87 +620,81 @@ /* Can't call any functions that use the transfer buffer beyond this point: they will overwrite the data already in __tb. */ + tbuf_beg = tbuf_ptr = __tb; tbuf_len = _go32_info_block.size_of_transfer_buffer; tbuf_end = tbuf_ptr + tbuf_len - 1; - /* If called from `system' and we have a command line shorter - than the DOS limit, we don't need to use !proxy at all. - Note that v1.x programs are always run through !proxy, - to prevent go32.exe from printing its copyright line. */ - if (!__dosexec_in_system || !v2_0 || cmdp - cmdline > CMDLEN_LIMIT) + /* LB: Starting from DJGPP v2.04, programs are always run through !proxy. + This allows correctly handle symlinks to .exes. */ + if (!check_talloc(found_si ? + type->stubinfo->struct_length : 0 + + (argc+1)*sizeof(short))) { - if (!check_talloc(found_si ? - type->stubinfo->struct_length : 0 - + (argc+1)*sizeof(short))) - { - argv[0] = save_argv0; - return -1; - } - if (found_si) - { - si_la = talloc(type->stubinfo->struct_length); - si_off = si_la - tbuf_beg; - dosmemput(sip, type->stubinfo->struct_length, si_la); - } + argv[0] = save_argv0; + return -1; + } + if (found_si) + { + si_la = talloc(type->stubinfo->struct_length); + si_off = si_la - tbuf_beg; + dosmemput(sip, type->stubinfo->struct_length, si_la); + } - rm_off = argv_off = talloc((argc+1) * sizeof(short)) - tbuf_beg; + rm_off = argv_off = talloc((argc+1) * sizeof(short)) - tbuf_beg; #if 0 - /* `alloca' could be dangerous with long command lines. We - will instead move the offsets one by one with `_farpokew'. */ - rm_argv = (short *)alloca((argc+1) * sizeof(short)); + /* `alloca' could be dangerous with long command lines. We + will instead move the offsets one by one with `_farpokew'. */ + rm_argv = (short *)alloca((argc+1) * sizeof(short)); #endif - for (i=0; i> 4), rm_off & 0xffff, - (unsigned)(tbuf_beg >> 4), si_off & 0xffff); - if (!found_si) - proxy_cmdline[22] = 0; /* remove stubinfo information */ - - if (__dosexec_in_system && v2_0) - pproxy = proxy_cmdline; - else + else /* not enough space to pass args */ { - /* `proxy_cmdline looks like an environment variable " !proxy=value". - This is used as the REAL command line specification by 2.01 - and later executables when called by `system'. But if that's - not the case, we need a blank instead of the `='. */ - proxy_cmdline[__PROXY_LEN] = ' '; - pcmd = proxy_cmdline; + argv[0] = save_argv0; + return -1; } } + + _farpokew (_dos_ds, tbuf_beg + argv_off, 0); + argv_off += sizeof(short); + + argv[0] = save_argv0; + /* Environment variables are all malloced. */ + proxy_cmdline = (char *)malloc (34); + if (!proxy_cmdline) + return -1; + + sprintf(proxy_cmdline, "%s=%04x %04x %04x %04x %04x", + __PROXY, argc, + (unsigned)(tbuf_beg >> 4), rm_off & 0xffff, + (unsigned)(tbuf_beg >> 4), si_off & 0xffff); + if (!found_si) + proxy_cmdline[22] = 0; /* remove stubinfo information */ + + if (__dosexec_in_system && v2_0) + pproxy = proxy_cmdline; else - argv[0] = save_argv0; + { + /* `proxy_cmdline looks like an environment variable " !proxy=value". + This is used as the REAL command line specification by 2.01 + and later executables when called by `system'. But if that's + not the case, we need a blank instead of the `='. */ + proxy_cmdline[__PROXY_LEN] = ' '; + pcmd = proxy_cmdline; + } retval = direct_exec_tail(rpath, pcmd, envp, pproxy, lfn); if (proxy_cmdline) @@ -709,9 +710,14 @@ int cmdlen; int i; int was_quoted = 0; /* was the program name quoted? */ + char real_program[FILENAME_MAX]; + + /* LB: solve symlinks here */ + if (!_solve_symlinks(program, real_program)) + return -1; /* Add spare space for possible quote characters. */ - cmdlen = strlen(program) + 4 + 2; + cmdlen = strlen(real_program) + 4 + 2; for (i=0; argv[i]; i++) cmdlen += 2*strlen(argv[i]) + 1; cmdline = (char *)alloca(cmdlen); @@ -719,21 +725,21 @@ /* FIXME: is this LFN-clean? What special characters can the program name have and how should they be quoted? */ strcpy(cmdline, "/c "); - if (strchr(program, ' ') || strchr(program, '\t')) + if (strchr(real_program, ' ') || strchr(real_program, '\t')) { was_quoted = 1; cmdline[3] = '"'; } - for (i = 0; program[i] > ' '; i++) + for (i = 0; real_program[i] > ' '; i++) { /* COMMAND.COM cannot grok program names with forward slashes. */ - if (program[i] == '/') + if (real_program[i] == '/') cmdline[i+3+was_quoted] = '\\'; else - cmdline[i+3+was_quoted] = program[i]; + cmdline[i+3+was_quoted] = real_program[i]; } - for (; program[i]; i++) - cmdline[i+3+was_quoted] = program[i]; + for (; real_program[i]; i++) + cmdline[i+3+was_quoted] = real_program[i]; if (was_quoted) { cmdline[i+3+was_quoted] = '"'; @@ -813,7 +819,8 @@ char line[130], interp[FILENAME_MAX], iargs[130]; FILE *f; char **newargs; - int i, hasargs=0; + int hasargs=0; + unsigned i; char *base, *p; int has_extension = 0, has_drive = 0; char pinterp[FILENAME_MAX]; diff -u -r -N --minimal djgpp.old/src/libc/posix/dirent/opendir.c djgpp/src/libc/posix/dirent/opendir.c --- djgpp.old/src/libc/posix/dirent/opendir.c Sun Nov 15 14:48:38 1998 +++ djgpp/src/libc/posix/dirent/opendir.c Wed Dec 8 16:25:00 1999 @@ -1,8 +1,10 @@ +/* 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 @@ -14,6 +16,7 @@ #include #include #include +#include #include "dirstruc.h" void @@ -52,7 +55,14 @@ opendir(const char *name) { int length; - DIR *dir = (DIR *)malloc(sizeof(DIR)); + DIR *dir; + char name_copy[FILENAME_MAX + 1]; + + /* LB: symlink support here. */ + if (!_solve_symlinks(name, name_copy)) + return NULL; + + dir = (DIR *)malloc(sizeof(DIR)); if (dir == 0) return 0; dir->num_read = 0; @@ -68,7 +78,7 @@ dir->flags |= __OPENDIR_PRESERVE_CASE; /* Make absolute path */ - _fixpath(name, dir->name); + _fixpath(name_copy, dir->name); /* Ensure that directory to be accessed exists */ if (access(dir->name, D_OK)) diff -u -r -N --minimal djgpp.old/src/libc/posix/fcntl/open.c djgpp/src/libc/posix/fcntl/open.c --- djgpp.old/src/libc/posix/fcntl/open.c Thu Jun 3 19:27:38 1999 +++ djgpp/src/libc/posix/fcntl/open.c Sat Nov 6 21:54:06 1999 @@ -4,6 +4,8 @@ /* 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 @@ -58,7 +60,18 @@ fd = _creatnew(filename, dmode, oflag & 0xff); else { - fd = _open(filename, oflag); + char real_name[FILENAME_MAX]; + + if (!(oflag & O_NOLINK)) + { + /* LB: symlink support here. */ + if (!_solve_symlinks(filename, real_name)) + return -1; + } + else + strcpy(real_name, filename); + + fd = _open(real_name, oflag); if (fd == -1) { @@ -67,7 +80,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, diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/chmod.c djgpp/src/libc/posix/sys/stat/chmod.c --- djgpp.old/src/libc/posix/sys/stat/chmod.c Mon Aug 28 05:42:36 1995 +++ djgpp/src/libc/posix/sys/stat/chmod.c Sun Nov 7 16:49:00 1999 @@ -1,13 +1,22 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ +#include #include #include #include +#include int chmod(const char *filename, int pmode) { int dmode; - unsigned attr = _chmod(filename, 0, 0); + char real_name[FILENAME_MAX]; + int attr; + + if (!_solve_symlinks(filename, real_name)) + return -1; + + attr = _chmod(real_name, 0, 0); if (attr == -1) return -1; @@ -19,7 +28,7 @@ /* Must clear the directory and volume bits, otherwise 214301 fails. Unused bits left alone (some network redirectors use them). */ - if (_chmod(filename, 1, (attr & 0xffe6) | dmode) == -1) + if (_chmod(real_name, 1, (attr & 0xffe6) | dmode) == -1) return -1; return 0; } diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/is_exec.c djgpp/src/libc/posix/sys/stat/is_exec.c --- djgpp.old/src/libc/posix/sys/stat/is_exec.c Sat Mar 20 22:59:52 1999 +++ djgpp/src/libc/posix/sys/stat/is_exec.c Wed Dec 8 16:20:50 1999 @@ -1,3 +1,4 @@ +/* Copyright (C) 1999 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 */ /* IS_EXEC.C @@ -26,6 +27,7 @@ #include #include #include +#include extern unsigned short _djstat_flags; unsigned short _get_magic(const char *, int); @@ -166,10 +168,13 @@ int _is_executable(const char *filename, int fhandle, const char *extension) { - if (!extension && filename) + char real_file_name[FILENAME_MAX + 1]; + if (!_solve_symlinks(filename, real_file_name)) + return -1; + if (!extension && real_file_name) { const char *cp, *ep=0; - for (cp=filename; *cp; cp++) + for (cp=real_file_name; *cp; cp++) { if (*cp == '.') ep = cp; @@ -181,7 +186,7 @@ if ((_djstat_flags & _STAT_EXEC_EXT) == 0 && extension && *extension - && strlen(extension) <= ((extension[0]=='.') ? 4 : 3)) + && strlen(extension) <= ((extension[0]=='.') ? 4U : 3U)) { /* Search the list of extensions in executables[]. */ char tmp_buf[6], *tp = tmp_buf; @@ -209,7 +214,7 @@ TWO CHARACTERS, lose here. Sorry, folks. */ if ( (_djstat_flags & _STAT_EXEC_MAGIC) == 0 ) { - switch (_get_magic(filename, fhandle)) + switch (_get_magic(real_file_name, fhandle)) { case 0x5a4d: /* "MZ" */ case 0x010b: diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/makefile djgpp/src/libc/posix/sys/stat/makefile --- djgpp.old/src/libc/posix/sys/stat/makefile Wed Apr 5 07:59:56 1995 +++ djgpp/src/libc/posix/sys/stat/makefile Sat Sep 25 22:12:30 1999 @@ -6,6 +6,7 @@ SRC += fixpath.c SRC += fstat.c SRC += is_exec.c +SRC += lstat.c SRC += mkdir.c SRC += mkfifo.c SRC += st_loss.c diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/mkdir.c djgpp/src/libc/posix/sys/stat/mkdir.c --- djgpp.old/src/libc/posix/sys/stat/mkdir.c Sun Jul 12 17:29:20 1998 +++ djgpp/src/libc/posix/sys/stat/mkdir.c Wed Dec 15 12:49:26 1999 @@ -1,7 +1,9 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 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 @@ -11,15 +13,21 @@ #include #include #include - +#include + int mkdir(const char *mydirname, mode_t mode) { __dpmi_regs r; int use_lfn = _USE_LFN; unsigned attr; + char dir_name[FILENAME_MAX + 1]; - _put_path(mydirname); + /* LB: symlink support here. */ + if (!_solve_symlinks(mydirname, dir_name)) + return -1; + + _put_path(dir_name); if(use_lfn) r.x.ax = 0x7139; @@ -47,7 +55,7 @@ { /* see if the directory existed, in which case we should return EEXIST - DJ */ - if (access(mydirname, D_OK) == 0) + if (access(dir_name, D_OK) == 0) errno = EEXIST; else errno = save_errno; @@ -56,12 +64,12 @@ } /* mkdir is stub'd, and we don't want to stub chmod also. */ - attr = _chmod(mydirname, 0, 0); + attr = _chmod(dir_name, 0, 0); /* Must clear the directory and volume bits, otherwise 214301 fails. Unused bits left alone (some network redirectors use them). Only care about read-only attribute. */ - if (_chmod(mydirname, 1, (attr & 0xffe6) | ((mode & S_IWUSR) == 0)) == -1) + if (_chmod(dir_name, 1, (attr & 0xffe6) | ((mode & S_IWUSR) == 0)) == -1) return -1; return 0; } diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/stat.c djgpp/src/libc/posix/sys/stat/stat.c --- djgpp.old/src/libc/posix/sys/stat/stat.c Thu Jun 3 19:27:40 1999 +++ djgpp/src/libc/posix/sys/stat/stat.c Wed Dec 8 16:25:52 1999 @@ -3,950 +3,26 @@ /* 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 */ -/* This is file STAT.C */ -/* - * Almost a 100% U**X-compatible stat() substitute. - * - * Usage: - * - * That's easy: put this into libc.a, then just call stat() as usual. - * - * Rationale: - * - * Many Unix-born programs make heavy use of stat() library - * function to make decisions on files' equality, size, access - * attributes etc. In the MS-DOS environment, many implementations - * of stat() are crippled, because DOS makes it very hard to get to - * certain pieces of information about files and directories. Thus - * porting a program to DOS is usually an exercise in #ifdef'ing. - * This implementation facilitates porting Unix programs to MS-DOS - * by providing stat() which is much more Unix-compatible than those - * of most DOS-based C compilers (e.g., Borland's). - * Specifically, the following issues are taken care of: - * - * 1. This stat() doesn't fail for root directories, returning - * valid information. - * 2. Directory size is not reported zero; the number of used - * directory entries multiplied by entry size is returned instead. - * 3. Mode bits are set for all 3 groups (user, group, other). - * 4. Directories are NOT reported read-only, unless one of R, H or S - * attributes is set. - * 5. Directories have their execute bit set, as they do under Unix. - * 6. Device names (such as /dev/con, lpt1, aux etc.) are treated as - * if they were on a special drive called `@:' (st_dev = -1). - * The "character special" mode bit is set for these devices. - * 7. The inode number (st_ino) is taken from the starting cluster - * number of the file. If the cluster number is unavailable, it - * is invented using the file's name in a manner that minimizes - * the possibility of inventing an inode which already belongs - * to another file. See below for details. - * 8. Executable files are found based on files' extensions and - * magic numbers present at their beginning, and their execute - * bits are set. - * - * Lossage: - * - * Beautiful as the above sounds, this implementation does fail - * under certain circumstances. The following is a list of known - * problems: - * - * 1. The time fields for a root directory cannot be obtained, so - * they are set to the beginning of the Epoch. - * 2. For files which reside on networked drives, the inode number - * is invented, because network redirectors usually do not - * bring that info with them. This is not a total lossage, but - * it could get us a different inode for each program run. - * 3. Empty files do not have a starting cluster number, because - * DOS doesn't allocate one until you actually write something - * to a file. For these the inode is also invented. - * 4. If the st_ino field is a 16 bit number, the invented inode - * numbers are from 65535 and down, assuming that most disks have - * unused portions near their end. Valid cluster numbers are 16-bit - * unsigned integers, so a possibility of a clash exists, although - * the last 80 or more cluster numbers are unused on all drives - * I've seen. If the st_ino is 32 bit, then invented inodes are - * all greater than 64k, which totally eliminates a possibility - * of a clash with an actual cluster number. - * 5. The method of computing directory size is an approximation: - * a directory might consume much more space, if it has many - * deleted entries. Still, this is a close approximation, and - * it does follow the logic of reporting size for a regular file: - * only the actually used space is returned. - * 6. As this implementation relies heavily on undocumented DOS - * features, it will fail to get actual file info in environments - * other than native DOS, such as DR-DOS, OS/2 etc. For these, - * the function will return whatever info is available with - * conventional DOS calls, which is no less than any other - * implementation could do. This stat() might also fail for - * future DOS versions, if the layout of internal DOS data - * area is changed; however, this seems unlikely. - * - * Copyright (c) 1994-96 Eli Zaretskii - * - * This software may be used freely so long as this copyright notice is - * left intact. There is no warranty on this software. - * - */ -/* - * Tested with DJGPP port of GNU C compiler, versions 1.11maint5 and 1.12, - * under MS-DOS 3.3, 4.01, 5.0, 6.20 (with and without DoubleSpace) and - * with networked drives under XFS 1.86, Novell Netware 3.22, and - * TSoft NFS 0.24Beta. - * +/* Main entry point. This is library stat() function. + LB: actual code moved to lstat() in lstat.c - this one only + supports symlinks */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "xstat.h" - -int __getdisk(void); -int __findfirst(const char *, struct ffblk *, int); -int __findnext(struct ffblk *); - -#define ALL_FILES (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_DIREC|FA_ARCH) - -#define _STAT_INODE 1 /* should we bother getting inode numbers? */ -#define _STAT_EXEC_EXT 2 /* get execute bits from file extension? */ -#define _STAT_EXEC_MAGIC 4 /* get execute bits from magic signature? */ -#define _STAT_DIRSIZE 8 /* compute directory size? */ -#define _STAT_ROOT_TIME 0x10 /* try to get root dir time stamp? */ -#define _STAT_WRITEBIT 0x20 /* fstat() needs write bit? */ - -/* Should we bother about executables at all? */ -#define _STAT_EXECBIT (_STAT_EXEC_EXT | _STAT_EXEC_MAGIC) - -/* The structure of the full directory entry. This is the 32-byte - record present for each file/subdirectory in a DOS directory. - Although the ``packed'' attribute seems to be unnecessary, I use - it to be sure it will still work for future versions of GCC. */ - -struct full_dirent { - char fname[8] __attribute__ ((packed)); - char fext[3] __attribute__ ((packed)); - unsigned char fattr __attribute__ ((packed)); - unsigned char freserved[10] __attribute__ ((packed)); - unsigned short ftime __attribute__ ((packed)); - unsigned short fdate __attribute__ ((packed)); - unsigned short fcluster __attribute__ ((packed)); - unsigned int fsize __attribute__ ((packed)); -}; - - -/* Static variables to speed up SDA DOS Swappable Data Area access on - subsequent calls. */ - -/* The count of number of SDA's we have. It is more than 1 for DOS - 4.x only. If it has a value of 0, the function init_dirent_table() - will be called to compute the addresses where we are to look for - directory entry of our file. A value of -1 means this method is - unsupported for this version of DOS. */ -static int dirent_count; - -/* The table of places to look for our directory entry. - Each entry in the table is a linear offset from the beginning of - conventional memory which points to a particular location within - one of the SDA's, where the entry of a file being stat()'ed could - appear. The offsets are computed once (when the routine is first - called) and then reused for other calls. The actual storage for - the table is malloc()'ed when this function is first called. */ -static unsigned int * dirent_table; - -/* When we have only one SDA, this is where its only place to look for - directory entry is stored. */ -static unsigned int dirent_place; - -/* This holds the fail bits from the last call to init_dirent_table(), - so we can return them every time get_inode_from_sda() is called. */ -static unsigned short init_dirent_table_bits; - -/* Holds the last seen value of __bss_count, to be safe for - restarted programs (emacs). */ -static int stat_count = -1; - -/* - * Parts of the following code is derived from file DOSSWAP.C, - * which came with ``Undocumented DOS'', 1st edition. - */ - -/* Compute table of pointers to look for directory entry of a file. */ -static int -init_dirent_table (void) -{ - short get_sda_func; - unsigned short dirent_offset; - unsigned short true_dos_version; - unsigned short dos_major, dos_minor; - __dpmi_regs regs; - - if (dirent_count == -1) /* we already tried and found we can't */ - return 0; - - /* Compute INT 21h function number and offset of directory entry - from start of SDA. These depend on the DOS version. - We need exact knowledge about DOS internals, so we need the - TRUE DOS version (not the simulated one by SETVER), if that's - available. */ - true_dos_version = _get_dos_version(1); - dos_major = true_dos_version >> 8; - dos_minor = true_dos_version & 0xff; - - if ((dos_major == 3) && (dos_minor >= 10)) - { - get_sda_func = 0x5d06; - dirent_offset = 0x1a7; - } - else if (dos_major == 4) - { - /* According to ``Undocumented DOS, 2nd edition'', I could have - used 5d06 here, as for DOS 5 and above, but I like to be - defensive. In fact, the above book itself uses 5d0b, contrary - to its own recommendation. */ - get_sda_func = 0x5d0b; - dirent_offset = 0x1b3; - } - else if (dos_major >= 5) - { - get_sda_func = 0x5d06; - dirent_offset = 0x1b3; - } - else - { - _djstat_fail_bits |= _STFAIL_OSVER; - dirent_count = -1; - return 0; - } - - _djstat_fail_bits &= ~_STFAIL_OSVER; /* version is OK */ - - /* Get the pointer to SDA by calling undocumented function 5dh of INT 21. */ - regs.x.ax = get_sda_func; - __dpmi_int(0x21, ®s); - if (regs.x.flags & 1) - { - _djstat_fail_bits |= _STFAIL_SDA; - dirent_count = -1; /* if the call failed, never try this later */ - return 0; - } - - _djstat_fail_bits &= ~_STFAIL_SDA; /* Get SDA succeeded */ - - /* DOS 4.x might have several SDA's, which means we might have more - than one place to look into. (It is typical of DOS 4 to complicate - things.) - Compute all the possible addresses where we will have to look. */ - if (dos_major == 4) - { - /* The pointer returned by INT 21h, AX=5D0b points to a header - which holds a number of SDA's and then an array of that number - of records each one of which includes address of an SDA (DWORD) - and its length and type (encoded in a WORD). - While walking this list of SDA's, we add to each pointer the - offset of directory entry and stash the resulting address in - our table for later use. */ - - int sda_list_walker = MK_FOFF(regs.x.ds, regs.x.si); - int i; - int *tbl; - - dirent_count = _farpeekw(_dos_ds, sda_list_walker); /* number of SDA's */ - - /* Allocate storage for table. */ - tbl = dirent_table = (int *)malloc(dirent_count*sizeof(int)); - if (!dirent_table) - { - /* If malloc() failed, maybe later it will succeed, so don't - store -1 in dirent_count. */ - dirent_count = 0; - _djstat_fail_bits |= _STFAIL_DCOUNT; - return 0; - } - - memset(dirent_table, 0, dirent_count*sizeof(int)); - _djstat_fail_bits &= ~_STFAIL_DCOUNT; /* dirent_count seems OK */ - - /* Walk the array of pointers, computing addresses of directory - entries and stashing them in our table. */ - _farsetsel(_dos_ds); - for (i = dirent_count, sda_list_walker += 2; i--; sda_list_walker += 6) - { - int sda_start = _farnspeekl(sda_list_walker); - unsigned short sda_len = _farnspeekw(sda_list_walker + 4) & 0x7fff; - - /* Let's be defensive here: if this SDA is too short to have - place for directory entry, we won't use it. */ - if (sda_len > dirent_offset) - *tbl++ = sda_start + dirent_offset; - else - dirent_count--; - } - } - - /* DOS 3.1 and 5.0 or later. We have only one SDA pointed to by - whatever INT 21h, AH=5d returns. */ - else - { - dirent_count = 1; - dirent_place = MK_FOFF(regs.x.ds, regs.x.si) + dirent_offset; - dirent_table = &dirent_place; - } - - return 1; -} - -/* Get inode number by searching DOS Swappable Data Area. - The entire directory entry for a file found by FindFirst/FindNext - appears at a certain (version-dependent) offset in the SDA after - one of those function is called. - Should be called immediately after calling DOS FindFirst function, - before the info is overwritten by somebody who calls it again. */ -static unsigned int -get_inode_from_sda(const char *mybasename) -{ - int count = dirent_count; - unsigned int * dirent_p = dirent_table; - unsigned short dos_mem_base = _dos_ds; - unsigned short our_mem_base = _my_ds(); - char * dot = strchr(mybasename, '.'); - size_t total_len = strlen(mybasename); - int name_len = dot ? dot - mybasename : total_len; - int ext_len = dot ? total_len - name_len - 1 : 0; - int cluster_offset = offsetof(struct full_dirent, fcluster); - - /* Restore failure bits set by last call to init_dirent_table(), so - they will be reported as if it were called now. */ - _djstat_fail_bits |= init_dirent_table_bits; - - /* Force reinitialization in restarted programs (emacs). */ - if (stat_count != __bss_count) - { - stat_count = __bss_count; - dirent_count = 0; - } - - /* Initialize the table of SDA entries where we are to look for - our file. */ - if (!dirent_count && !init_dirent_table()) - { - /* Don't save the truename failure bit. */ - init_dirent_table_bits = (_djstat_fail_bits & ~_STFAIL_TRUENAME); - return 0; - } - init_dirent_table_bits = (_djstat_fail_bits & ~_STFAIL_TRUENAME); - if (dirent_count == -1) - return 0; - - count = dirent_count; - dirent_p = dirent_table; - - _farsetsel(dos_mem_base); - - /* This is DOS 4.x lossage: this loop might execute many times. - For other DOS versions it is executed exactly once. */ - while (count--) - { - unsigned int src_address = *dirent_p; - char cmp_buf[sizeof(struct full_dirent)]; - - /* Copy the directory entry from the SDA to local storage. - The filename is stored there in infamous DOS format: name and - extension blank-padded to 8/3 characters, no dot between them. */ - movedata(dos_mem_base, src_address, our_mem_base, (unsigned int)cmp_buf, - sizeof(struct full_dirent)); - - /* If this is the filename we are looking for, return - its starting cluster. */ - if (!strncmp(cmp_buf, mybasename, name_len) && - (ext_len == 0 || !strncmp(cmp_buf + 8, dot + 1, ext_len))) - return (unsigned int)_farnspeekw(*dirent_p + cluster_offset); - - /* This is not our file. Search more, if more addresses left. */ - dirent_p++; - } - - /* If not found, give up. */ - _djstat_fail_bits |= _STFAIL_BADSDA; - return 0; -} - -int _ioctl_get_first_cluster(const char *); - -/* Get the number of the first cluster of PATHNAME using - the IOCTL call Int 21h/AX=440Dh/CX=0871h, if that call - is supported by the OS. Return the cluster number, or - a negative number if this service isn't supported. */ - -int -_ioctl_get_first_cluster(const char *pathname) -{ - __dpmi_regs r; - - /* See if the IOCTL GetFirstCluster call is supported. */ - r.x.ax = 0x4411; /* query generic IOCTL capability by drive */ - r.h.bl = pathname[0] & 0x1f; /* drive number (1=A:) */ - r.x.cx = 0x871; - __dpmi_int(0x21, &r); - if ((r.x.flags & 1) == 0 && r.x.ax == 0) - { - r.x.ax = 0x440d; /* Generic IOCTL */ - r.x.cx = 0x0871; /* category code 08h, minor code 71h */ - r.x.bx = 1; /* pathname uses current OEM character set */ - r.x.ds = __tb >> 4; - r.x.dx = __tb & 0x0f; - _put_path(pathname); - __dpmi_int(0x21, &r); - if ((r.x.flags & 1) == 0) - return ( ((int)r.x.dx << 16) + r.x.ax ); - } - return -1; -} - -static char blanks_8[] = " "; - -static int -stat_assist(const char *path, struct stat *statbuf) -{ - struct ffblk ff_blk; - char canon_path[MAX_TRUE_NAME]; - char pathname[MAX_TRUE_NAME]; - short drv_no; - unsigned dos_ftime; - - _djstat_fail_bits = 0; - - memset(statbuf, 0, sizeof(struct stat)); - memset(&dos_ftime, 0, sizeof(dos_ftime)); - - /* Fields which are constant under DOS. */ - statbuf->st_uid = getuid(); - statbuf->st_gid = getgid(); - statbuf->st_nlink = 1; -#ifndef NO_ST_BLKSIZE - statbuf->st_blksize = _go32_info_block.size_of_transfer_buffer; -#endif - - /* Make the path explicit. This makes the rest of our job much - easier by getting rid of some constructs which, if present, - confuse `_truename' and/or `findfirst'. In particular, it - deletes trailing slashes, makes "d:" explicit, and allows us - to make an illusion of having a ".." entry in root directories. */ - _fixpath (path, pathname); - - /* Get the drive number. It is always explicit, since we - called `_fixpath' on the original pathname. */ - drv_no = toupper((unsigned char)pathname[0]) - 'A'; - - /* Produce canonical pathname, with all the defaults resolved and - all redundant parts removed. This calls undocumented DOS - function 60h. */ - if (_truename(path, canon_path) || _truename(pathname, canon_path)) - { - /* Detect character device names which must be treated specially. - We could simply call FindFirst and test the 6th bit, but some - versions of DOS have trouble with this (see Ralph Brown's - Interrupt List, ``214E'', under `Bugs'). Instead we use - truename() which calls INT 21/AX=6000H. For character devices - it returns X:/DEVNAME, where ``X'' is the current drive letter - (note the FORWARD slash!). E.g., for CON or \dev\con it will - return C:/CON. - We will pretend that devices all reside on a special drive - called `@', which corresponds to st_dev = -1. This is because - these devices have no files, and we must invent inode numbers - for them; this scheme allows to lower a risk of clash between - invented inode and one which belongs to a real file. This is - also compatible with what our fstat() does. - */ - char_dev: - if (canon_path[2] == '/') - { - char dev_name[9]; /* devices are at most 8 characters long */ - - strncpy(dev_name, canon_path + 3, 8); /* the name without `X:/' */ - dev_name[8] = '\0'; - strcpy(canon_path, "@:\\dev\\"); - strcat(canon_path, dev_name); - strncat(canon_path, blanks_8, 8 - strlen(dev_name)); /* blank-pad */ - canon_path[15] = '\0'; /* ensure zero-termination */ - - /* Invent inode */ - statbuf->st_ino = _invent_inode(canon_path, 0, 0); - - /* Device code. */ - statbuf->st_dev = -1; -#ifdef HAVE_ST_RDEV - statbuf->st_rdev = -1; -#endif - - /* Set mode bits, including character special bit. - Should we treat printer devices as write-only? */ - statbuf->st_mode |= (S_IFCHR | READ_ACCESS | WRITE_ACCESS); - - /* We will arrange things so that devices have current time in - the access-time and modified-time fields of struct stat, and - zero (the beginning of times) in creation-time field. This - is consistent with what DOS FindFirst function returns for - character device names (if it succeeds--see above). */ - statbuf->st_atime = statbuf->st_mtime = time(0); - statbuf->st_ctime = _file_time_stamp(dos_ftime); - - return 0; - } - else if (canon_path[0] >= 'A' && canon_path[0] <= 'z' && - canon_path[1] == ':' && canon_path[2] == '\\') - { - /* _truename() returned a name with a drive letter. (This is - always so for local drives, but some network redirectors - also do this.) We will take this to be the TRUE drive - letter, because _truename() knows about SUBST and JOIN. - If the canonicalized path returns in the UNC form (which - means the drive is remote), it cannot be SUBSTed or JOINed, - because SUBST.EXE and JOIN.EXE won't let you do it; so, for - these cases, there is no problem in believing the drive - number we've got from the original path (or is there?). */ - drv_no = toupper((unsigned char)canon_path[0]) - 'A'; - } - } - else - { - /* _truename() failed. (This really shouldn't happen, but who knows?) - At least uppercase all letters, convert forward slashes to backward - ones, and pray... */ - register const char *src = pathname; - register char *dst = canon_path; - - while ( (*dst = (*src > 'a' && *src < 'z' - ? *src++ - ('a' - 'A') - : *src++)) != '\0') - { - if (*dst == '/') - *dst = '\\'; - dst++; - } - - _djstat_fail_bits |= _STFAIL_TRUENAME; - } - - /* Call DOS FindFirst function, which will bring us most of the info. */ - if (!__findfirst(pathname, &ff_blk, ALL_FILES)) - { - /* Time fields. */ - dos_ftime = - ( (unsigned short)ff_blk.ff_fdate << 16 ) + - (unsigned short)ff_blk.ff_ftime; - - /* If the IOCTL GetFirstCluster call is available, try it first. */ - if ( (_djstat_flags & _STAT_INODE) == 0 - && (statbuf->st_ino = _ioctl_get_first_cluster(pathname)) <= 0) - { - - /* For networked drives, don't believe the starting cluster - that the network redirector feeds us; always invent inode. - This is because network redirectors leave bogus values there, - and we don't have enough info to decide if the starting - cluster value is real or just a left-over from previous call. - For local files, try first using DOS SDA to get the inode from - the file's starting cluster number; if that fails, invent inode. - Note that the if clause below tests for non-zero value returned - by is_remote_drive(), which includes possible failure (-1). - This is because findfirst() already succeeded for our pathname, - and therefore the drive is a legal one; the only possibility that - is_remote_drive() fails is that some network redirector takes - over IOCTL functions in an incompatible way, which means the - drive is remote. QED. */ - if (statbuf->st_ino == 0 /* don't try SDA if IOCTL call succeeded */ - || _is_remote_drive(drv_no) - || (statbuf->st_ino = get_inode_from_sda(ff_blk.ff_name)) == 0) - { - _djstat_fail_bits |= _STFAIL_HASH; - statbuf->st_ino = - _invent_inode(canon_path, dos_ftime, ff_blk.ff_fsize); - } - else if (toupper ((unsigned char)canon_path[0]) != toupper ((unsigned char)pathname[0]) - && canon_path[1] == ':' - && canon_path[2] == '\\' - && canon_path[3] == '\0') - /* The starting cluster in SDA for the root of JOINed drive - actually belongs to the directory where that drive is - ``mounted''. This can potentially be the cluster of - another file on the JOINed drive. We cannot allow this. */ - statbuf->st_ino = 1; - } - - /* File size. */ - statbuf->st_size = ff_blk.ff_fsize; - - /* Mode bits. */ - statbuf->st_mode |= READ_ACCESS; - if ( !(ff_blk.ff_attrib & 0x07) ) /* no R, H or S bits set */ - statbuf->st_mode |= WRITE_ACCESS; - - /* Sometimes `_truename' doesn't return X:/FOO for character - devices. However, FindFirst returns attribute 40h for them. */ - if (ff_blk.ff_attrib == 0x40) - { - size_t cplen = strlen (canon_path); - char *pslash = canon_path + cplen - 1; - - while (pslash > canon_path + 2 && *pslash != '\\') - pslash--; - - /* Force it into X:/FOO form. */ - if (canon_path[1] == ':') - { - if (pslash > canon_path + 2) - memmove (canon_path + 2, pslash, - cplen - (pslash - canon_path) + 1); - canon_path[2] = '/'; - goto char_dev; - } - } - - /* Directories should have Execute bits set. */ - if (ff_blk.ff_attrib & 0x10) - statbuf->st_mode |= (S_IFDIR | EXEC_ACCESS); - - else - { - /* This is a regular file. */ - char *extension = strrchr(ff_blk.ff_name, '.'); - - /* Set regular file bit. */ - statbuf->st_mode |= S_IFREG; - - if ((_djstat_flags & _STAT_EXECBIT) != _STAT_EXECBIT) - { - /* Set execute bits based on file's extension and - first 2 bytes. */ - if (extension) - extension++; /* get past the dot */ - if (_is_executable(pathname, -1, extension)) - statbuf->st_mode |= EXEC_ACCESS; - } - } - } - else if ((_djstat_fail_bits & _STFAIL_TRUENAME)) - { - /* If both `findfirst' and `_truename' failed, this must - be a non-existent file or an illegal/inaccessible drive. */ - if (errno == ENMFILE) - errno = ENODEV; - return -1; - } - else if (pathname[3] == '\0') - { - /* Detect root directories. These are special because, unlike - subdirectories, FindFirst fails for them. We look at PATHNAME - because a network redirector could tweak what `_truename' - returns to be utterly unrecognizable as root directory. PATHNAME - always begins with "d:/", so it is root if PATHNAME[3] = 0. */ - - /* Mode bits. */ - statbuf->st_mode |= (S_IFDIR|READ_ACCESS|WRITE_ACCESS|EXEC_ACCESS); - - /* Root directory will have an inode = 1. Valid cluster numbers - for real files under DOS start with 2. */ - statbuf->st_ino = 1; - - /* Simulate zero size. This is what FindFirst returns for every - sub-directory. Later we might compute a better approximation - (see below). */ - ff_blk.ff_fsize = 0L; - - /* The time fields are left to be zero, unless the user wants us - to try harder. In the latter case, we check if the root has - a volume label entry, and use its time if it has. */ - - if ( (_djstat_flags & _STAT_ROOT_TIME) == 0 ) - { - char buf[7]; - int volume_found = 0; - - strcpy(buf, pathname); - strcat(buf, "*.*"); - /* Floppies written by Windows 9X and NT include entries - that have volume label bit set, but are actually parts - of an LFN entry. Non-LFN platforms might be fooled to - take them as volume labels, and report bogus time stamps. */ - volume_found = __findfirst(buf, &ff_blk, FA_LABEL) == 0; - while (volume_found - && (ff_blk.ff_attrib & (FA_HIDDEN|FA_SYSTEM)) != 0) - volume_found = __findnext(&ff_blk) == 0; - if (volume_found) - dos_ftime = ( (unsigned)ff_blk.ff_fdate << 16 ) + ff_blk.ff_ftime; - else - _djstat_fail_bits |= _STFAIL_LABEL; - } - } - else - { - int e = errno; /* errno value from original FindFirst on PATHNAME */ - int i = 0; - int j = strlen (pathname) - 1; - - /* Check for volume labels. We did not mix FA_LABEL with - other attributes in the call to `__findfirst' above, - because some environments will return bogus info in - that case. For instance, Win95 and WinNT seem to - ignore `pathname' and return the volume label even if it - doesn't fit the name in `pathname'. This fools us to - think that a non-existent file exists and is a volume - label. Hence we test the returned name to be PATHNAME. */ - if (!__findfirst(pathname, &ff_blk, FA_LABEL)) - { - i = strlen (ff_blk.ff_name) - 1; - - if (j >= i) - { - for ( ; i >= 0 && j >= 0; i--, j--) - if (toupper ((unsigned char)ff_blk.ff_name[i]) != toupper ((unsigned char)pathname[j])) - break; - } - } - - if (i < 0 && pathname[j] == '/') - { - /* Indeed a label. */ - statbuf->st_mode = READ_ACCESS; -#ifdef S_IFLABEL - statbuf->st_mode |= S_IFLABEL; -#endif - statbuf->st_ino = 1; - statbuf->st_size = 0; - dos_ftime = ( (unsigned)ff_blk.ff_fdate << 16 ) + ff_blk.ff_ftime; - } - else - { - /* FindFirst on volume labels might set errno to ENMFILE or even - to something more strange like EINVAl; correct that. */ - errno = e; /* restore errno from the original FindFirst */ - if (errno == ENMFILE) - errno = ENOENT; - return -1; - } - } - - /* Device code. */ - statbuf->st_dev = drv_no; -#ifdef HAVE_ST_RDEV - statbuf->st_rdev = drv_no; -#endif - - /* Time fields. */ - statbuf->st_atime = statbuf->st_mtime = statbuf->st_ctime = - _file_time_stamp(dos_ftime); - - if ( ! strcmp(ff_blk.lfn_magic,"LFN32") ) - { - unsigned xtime; - xtime = *(unsigned *)&ff_blk.lfn_ctime; - if(xtime) /* May be zero if file written w/o lfn active */ - statbuf->st_ctime = _file_time_stamp(xtime); - xtime = *(unsigned *)&ff_blk.lfn_atime; - if(xtime > dos_ftime) /* Accessed time is date only, no time */ - statbuf->st_atime = _file_time_stamp(xtime); - } - - if ( (statbuf->st_mode & S_IFMT) == S_IFDIR - && (_djstat_flags & _STAT_DIRSIZE) == 0 ) - { - /* Under DOS, directory entries for subdirectories have - zero size. Therefore, FindFirst brings us zero size - when called on a directory. (Some network redirectors - might do a better job, thus below we also test for zero size - actually being returned.) If we have zero-size directory, - we compute here the actual directory size by reading its - entries, then multiply their number by 32 (the size of a - directory entry under DOS). This might lose in the case - that many files were deleted from a once huge directory, - because AFAIK, directories don't return unused clusters to - the disk pool. Still, it is a good approximation of the - actual directory size. - - We also take this opportunity to return the number of links - for directories as Unix programs expect it to be: the number - of subdirectories, plus 2 (the directory itself and the ``.'' - entry). - - The (max) size of the root directory could also be taken from - the disk BIOS Parameter Block (BPB) which can be obtained - by calling IOCTL (INT 21/AH=44H), subfunction 0DH, minor - function 60H. But we will treat all directories the same, - even at performance cost, because it's more robust for - networked drives. */ - - size_t pathlen = strlen (pathname); - char lastc = pathname[pathlen - 1]; - char *search_spec = (char *)alloca (pathlen + 10); /* need only +5 */ - int nfiles = 0, nsubdirs = 0, done; - size_t extra = 0; - int add_extra = 0; - - strcpy(search_spec, pathname); - if (lastc == '/') - strcat(search_spec, "*.*"); - else - strcat(search_spec, "/*.*"); - - if (statbuf->st_size == 0 && _USE_LFN) - { - /* VFAT filesystems use additional directory entries to - store the long filenames. */ - char fstype[40]; - - if ((_get_volume_info(pathname,0,0,fstype) & _FILESYS_LFN_SUPPORTED) - && strncmp(fstype, "FAT", 4) == 0) - add_extra = 1; - } - - /* Count files and subdirectories. */ - for (done = __findfirst(search_spec, &ff_blk, ALL_FILES); - !done; - done = __findnext(&ff_blk)) - { - register char *fname = ff_blk.ff_name; - - /* Don't count "." and ".." entries. This will show empty - directories as size 0. */ - if (! (fname[0] == '.' - && (fname[1] == '\0' - || (fname[1] == '.' - && fname[2] == '\0')))) - { - char fn[13]; - - nfiles++; - if (ff_blk.ff_attrib & 0x10) - nsubdirs++; - /* For each 13 characters of the long filename, a - 32-byte directory entry is used. */ - if (add_extra && strcmp(_lfn_gen_short_fname(fname, fn), fname)) - extra += (strlen(fname) + 12) / 13; - } - } - - statbuf->st_nlink = nsubdirs + 2; - if (statbuf->st_size == 0) - statbuf->st_size = (nfiles + extra) * sizeof(struct full_dirent); - } - - return 0; -} - -/* Main entry point. This is library stat() function. - */ +#include +#include +#include int stat(const char *path, struct stat *statbuf) { - int e = errno; - int pathlen, ret; - - if (!path || !statbuf) - { - errno = EFAULT; - return -1; - } - - if ((pathlen = strlen (path)) >= MAX_TRUE_NAME) - { - errno = ENAMETOOLONG; - return -1; - } - - /* Fail if PATH includes wildcard characters supported by FindFirst, - or if it is empty. */ - if (memchr(path, '*', pathlen) || memchr(path, '?', pathlen) - || path[0] == '\0') - { - errno = ENOENT; /* since no such filename is possible */ + char name_copy[FILENAME_MAX + 1]; + + /* LB: symlink support here. */ + if (!_solve_symlinks(path, name_copy)) return -1; - } - - if (__FSEXT_call_open_handlers(__FSEXT_stat, &ret, &path)) - return ret; - - if (stat_assist(path, statbuf) == -1) - { - return -1; /* errno set by stat_assist() */ - } - else - { - errno = e; - return 0; - } -} - -#ifdef TEST - -unsigned short _djstat_flags = 0; - -void -main(int argc, char *argv[]) -{ - struct stat stat_buf; - char *endp; - - if (argc < 2) - { - fprintf (stderr, "Usage: %s <_djstat_flags> \n", argv[0]); - exit(0); - } - - if (stat(*argv, &stat_buf) != 0) - perror ("stat failed on argv[0]"); - else - fprintf(stderr, "DOS %d.%d (%s)\n", _osmajor, _osminor, _os_flavor); - argc--; argv++; - - _djstat_flags = (unsigned short)strtoul(*argv, &endp, 0); - argc--; argv++; - - while (argc--) - { - if (!stat(*argv, &stat_buf)) - { - fprintf(stderr, "%s: %d %6u %o %d %d %ld %lu %s", *argv, - stat_buf.st_dev, - (unsigned)stat_buf.st_ino, - stat_buf.st_mode, - stat_buf.st_nlink, - stat_buf.st_uid, - (long)stat_buf.st_size, - (unsigned long)stat_buf.st_mtime, - ctime(&stat_buf.st_mtime)); - _djstat_describe_lossage(stderr); - } - else - { - fprintf(stderr, "%s: lossage", *argv); - perror(" "); - _djstat_describe_lossage(stderr); - } - - ++argv; - } - - exit (0); + + return lstat(name_copy, statbuf); /* Real file */ } - -#endif diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/access.c djgpp/src/libc/posix/unistd/access.c --- djgpp.old/src/libc/posix/unistd/access.c Wed Aug 4 21:58:24 1999 +++ djgpp/src/libc/posix/unistd/access.c Wed Dec 8 16:25:06 1999 @@ -3,17 +3,27 @@ /* 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 -#include #include +#include +#include + int access(const char *fn, int flags) { - int attr = _chmod(fn, 0); + int attr; + char file_name[FILENAME_MAX + 1]; + + /* LB: symlink support here. */ + if (!_solve_symlinks(fn, file_name)) + return -1; + + attr = _chmod(file_name, 0); if (attr == -1) { struct ffblk ff; @@ -21,7 +31,7 @@ /* Root directories on some non-local drives (e.g. CD-ROM) might fail `_chmod'. `findfirst' to the rescue. */ - _fixpath(fn, fixed_path); + _fixpath(file_name, fixed_path); if (fixed_path[1] == ':' && fixed_path[2] == '/' && fixed_path[3] == 0) { char *fp = fixed_path + 3; @@ -41,7 +51,7 @@ /* Devices also fail `_chmod'; some programs won't write to a device unless `access' tells them they are writeable. */ - if (findfirst(fn, &ff, FA_RDONLY | FA_ARCH) == 0 + if (findfirst(file_name, &ff, FA_RDONLY | FA_ARCH) == 0 && (ff.ff_attrib & 0x40) == 0x40 && (flags & (X_OK | D_OK)) == 0) { diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/chdir.c djgpp/src/libc/posix/unistd/chdir.c --- djgpp.old/src/libc/posix/unistd/chdir.c Sun Dec 13 14:09:46 1998 +++ djgpp/src/libc/posix/unistd/chdir.c Thu Dec 9 17:38:24 1999 @@ -1,35 +1,46 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 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 #include #include #include +#include +#include +#include +#include int __chdir (const char *mydirname) { __dpmi_regs r; int drv_no = -1; + char real_name[FILENAME_MAX]; + char path[FILENAME_MAX]; + + /* LB: symlink support here. */ + if (!_solve_symlinks(mydirname, real_name)) + return -1; + _fixpath(real_name, path); - if (mydirname == 0) + if (path == 0) { errno = EINVAL; return -1; } - if (mydirname[0] == 0) + if (path[0] == 0) { errno = ENOENT; return -1; } - _put_path(mydirname); + _put_path(path); /* _put_path performs some magic conversions of file names, so the path in the transfer buffer can include a drive even though diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/chown.c djgpp/src/libc/posix/unistd/chown.c --- djgpp.old/src/libc/posix/unistd/chown.c Sun Jun 18 09:43:52 1995 +++ djgpp/src/libc/posix/unistd/chown.c Wed Dec 15 15:05:50 1999 @@ -11,7 +11,7 @@ int chown(const char *path, uid_t owner, gid_t group) { - if (!__file_exists(path)) /* non-existent file */ + if (access(path, F_OK)) /* non-existent file */ { errno = ENOENT; return -1; diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/getcwd.c djgpp/src/libc/posix/unistd/getcwd.c --- djgpp.old/src/libc/posix/unistd/getcwd.c Sun Sep 1 00:09:32 1996 +++ djgpp/src/libc/posix/unistd/getcwd.c Sat Dec 4 10:06:22 1999 @@ -1,11 +1,10 @@ +/* Copyright (C) 1999 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 #include #include @@ -19,7 +18,8 @@ { char *bp; __dpmi_regs r; - int needed_length, c; + size_t needed_length; + int c; unsigned use_lfn = _USE_LFN; int preserve_case = _preserve_fncase(); char *name_start; diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/lchown.c djgpp/src/libc/posix/unistd/lchown.c --- djgpp.old/src/libc/posix/unistd/lchown.c Thu Jan 1 00:00:00 1970 +++ djgpp/src/libc/posix/unistd/lchown.c Wed Dec 15 15:05:14 1999 @@ -0,0 +1,20 @@ +/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ +#include +#include +#include +#include + +/* MS-DOS couldn't care less about file ownerships, so we could + always succeed. At least fail for non-existent files + and for devices. */ + +int +lchown(const char *path, uid_t owner, gid_t group) +{ + if (!__file_exists(path)) /* non-existent file */ + { + errno = ENOENT; + return -1; + } + return 0; +} diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/lchown.txh djgpp/src/libc/posix/unistd/lchown.txh --- djgpp.old/src/libc/posix/unistd/lchown.txh Thu Jan 1 00:00:00 1970 +++ djgpp/src/libc/posix/unistd/lchown.txh Wed Dec 15 15:05:02 1999 @@ -0,0 +1,24 @@ +@node lchown, unix +@subheading Syntax + +@example +#include + +int lchown(const char *file, int owner, int group); +@end example + +@subheading Description + +This function does nothing under MS-DOS + +@subheading Return Value + +This function always returns zero if the file exists (it does not +follow symbolic links), else it returns -1 and sets @var{errno} +to @code{ENOENT}. + + +@subheading Portability + +@portability !ansi, posix + diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/makefile djgpp/src/libc/posix/unistd/makefile --- djgpp.old/src/libc/posix/unistd/makefile Fri Sep 20 01:40:46 1996 +++ djgpp/src/libc/posix/unistd/makefile Wed Dec 15 15:04:04 1999 @@ -32,12 +32,14 @@ SRC += getppid.c SRC += getuid.c SRC += isatty.c +SRC += lchown.c SRC += link.c SRC += lseek.c SRC += pathconf.c SRC += pause.c SRC += pipe.c SRC += read.c +SRC += readlink.c SRC += rmdir.c SRC += setgid.c SRC += setpgid.c @@ -49,5 +51,6 @@ SRC += ttyname.c SRC += unlink.s SRC += write.c +SRC += xsymlink.c include $(TOP)/../makefile.inc diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/readlink.c djgpp/src/libc/posix/unistd/readlink.c --- djgpp.old/src/libc/posix/unistd/readlink.c Thu Jan 1 00:00:00 1970 +++ djgpp/src/libc/posix/unistd/readlink.c Thu Dec 9 17:41:08 1999 @@ -0,0 +1,61 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xsymlink.h" + +int readlink(const char * filename, char * buffer, size_t size) +{ + ssize_t bytes_read = 0; + char buf[FILENAME_MAX]; + int fd; + struct ffblk file_info; + int old_errno; + char data_buf[_SYMLINK_FILE_LEN]; + + /* Now do the real job */ + /* First DJGPP symlink check - is file size a fixed ``magic value''? */ + if (findfirst(filename, &file_info, 0) || + (file_info.ff_fsize != _SYMLINK_FILE_LEN)) + { + errno = ENOENT; + return -1; + } + + /* Now we check for special DJGPP symlink format */ + old_errno = errno; + fd = open(filename, O_NOLINK | O_RDONLY | O_BINARY); + if (fd < 0) + return -1; /* errno from open() call */ + bytes_read = read(fd, buf, _SYMLINK_PREFIX_LEN); + if ((unsigned)bytes_read < _SYMLINK_PREFIX_LEN) /* The cast is safe */ + { + close(fd); + errno = EINVAL; + return -1; + } + if (strncmp(buf, _SYMLINK_PREFIX, _SYMLINK_PREFIX_LEN)) + { + close(fd); + errno = EINVAL; + return -1; + } + + /* Here we know that file is really DJGPP symlink, */ + /* so we extract file name it points to */ + bytes_read = read(fd, &data_buf, _SYMLINK_FILE_LEN); + close(fd); + if (bytes_read == -1) + return -1; /* Return errno set by read() */ + bytes_read = strchr(data_buf, '\n') - data_buf; + memcpy(buffer, data_buf, ((unsigned)bytes_read > size) ? size : bytes_read); + return bytes_read; +} + diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/readlink.txh djgpp/src/libc/posix/unistd/readlink.txh --- djgpp.old/src/libc/posix/unistd/readlink.txh Thu Jan 1 00:00:00 1970 +++ djgpp/src/libc/posix/unistd/readlink.txh Thu Dec 9 17:45:24 1999 @@ -0,0 +1,37 @@ +@node readlink, io +@subheading Syntax + +@example +#include + +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. +It is @strong{not} terminated @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]; +if (readlink("/djgpp/bin/sh.exe", buf, FILENAME_MAX) == -1) + if (errno == EINVAL) + puts("/djgpp/bin/sh.exe is not a symbolic link."); +@end example + diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/symlink.c djgpp/src/libc/posix/unistd/symlink.c --- djgpp.old/src/libc/posix/unistd/symlink.c Wed Aug 25 11:24:48 1999 +++ djgpp/src/libc/posix/unistd/symlink.c Thu Dec 9 18:39:10 1999 @@ -1,180 +1,52 @@ +/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ #include -#include -#include -#include +#include #include -#include #include -#include -#include -#include -#include -#include - -static char EXE_SUFFIX[] = ".exe"; -static char STUBIFY[] = "stubify.exe"; -static char STUBEDIT[] = "stubedit.exe"; - -/* Return a pointer to the tail of the pathname. */ -static const char * -tail (const char *path) -{ - const char *p = path && path[0] ? path + strlen (path) - 1 : path; - - if (p) - { - while (p > path && *p != '/' && *p != '\\' && *p != ':') - p--; - if (p > path) - p++; - } - return p; -} - -/* - This returns - -1, when the file does not exist - 0, when it is not a v2 executable - 1, when it is a v2 executable -*/ - -static int is_v2_prog(const char *program) -{ - const _v2_prog_type *type; - - type = _check_v2_prog (program, -1); - - if (!type->valid) - return -1; - - if (type->object_format != _V2_OBJECT_FORMAT_COFF) - return 0; - - if (type->version.v.major < 2) - return 0; +#include +#include - return 1; -} +#include "xsymlink.h" -/* Support the DJGPP ``symlinks'' for .exe files. */ -int -symlink (const char *source, const char *dest) +/* Support DJGPP symlinks for all files */ +int symlink(const char *source, const char *dest) { - char src_abs[FILENAME_MAX+5], src_short[FILENAME_MAX+5]; - char dest_abs[FILENAME_MAX+5]; - char *np, ropt[FILENAME_MAX+15]; /* some extra for ``runfile='' */ - const char *src_base, *dest_base; - - int v2_prog = 0; - - _fixpath (source, src_abs); - _fixpath (dest, dest_abs); - src_base = tail (src_abs); - dest_base = tail (dest_abs); + int symlink_file; + char real_dest[FILENAME_MAX]; + static char fill_buf[_SYMLINK_FILE_LEN + 1] = + "This is just a text to force symlink file to " + "be 510 bytes long. Do not delete it nor spaces " + "following it."; + memset(fill_buf + strlen(fill_buf), ' ', + _SYMLINK_FILE_LEN - strlen(fill_buf)); - /* DJGPP symlinks must be in the same directory. */ - if (src_base - src_abs != dest_base - dest_abs - || strnicmp (src_abs, dest_abs, src_base - src_abs)) - { - errno = EXDEV; + /* Common error conditions */ + if (!source || !dest) + { + errno = EINVAL; return -1; - } - - /* Any file is already a link to itself. */ - if (stricmp (src_abs, dest_abs) == 0) - return 0; - - /* Check at first, if the given name is a v2 executable (may be - unstubbed COFF image) */ - v2_prog = is_v2_prog(src_abs); - - /* It is an existing file but no v2 executable */ - if (v2_prog == 0) - { - errno = EXDEV; - return -1; - } - - /* Allow to say `ln -s src dest' when we really - mean `src.exe' and `dest.exe' */ - np = src_abs + strlen (src_abs); - if (np - src_abs > 4 && stricmp (np - 4, EXE_SUFFIX) != 0) - { - strcat (src_abs, EXE_SUFFIX); - /* Now test again for v2 executable, but only if not already - succeed. */ - v2_prog = v2_prog == 1 ? v2_prog : is_v2_prog(src_abs); - } - - /* It is an existing file but no v2 executable */ - if (v2_prog == 0) - { - errno = EXDEV; - return -1; - } - - /* When we are here, either the file exists and is a v2 executable - or it does not exist and we hope, the the user knows what he - does. */ - - /* Under LFN, we need the short version of the program name, since that - is what the stub stores (and what a program gets in its argv[0]). */ - if (_USE_LFN) - { - if (__file_exists (src_abs)) - { - /* File exists. Get its 8+3 alias. */ - __dpmi_regs r; - - dosmemput(src_abs, strlen (src_abs)+1, __tb); - r.x.ax = 0x7160; /* Truename */ - r.x.cx = 1; /* Get short name */ - r.x.ds = r.x.es = __tb / 16; - r.x.si = r.x.di = __tb & 15; - __dpmi_int(0x21, &r); - if (r.x.flags & 1 || r.x.ax == 0x7100) - /* Shouldn't happen: LFN *is* supported and file *does* exist. */ - { - errno = EIO; - return -1; - } - dosmemget (__tb, FILENAME_MAX, src_short); - } - else - { - /* File doesn't exist. Generate short name that would be used. - FIXME: this will lose if the generated name collides with - another file already in that directory; however, the only - alternative is to disallow symlinks to non-existing files. */ - char *p = strncpy (src_short, src_abs, src_base - src_abs); - _lfn_gen_short_fname (src_base, p + (src_base - src_abs)); - } - } - else - strcpy (src_short, src_abs); + } - /* Need the basename of SRC_SHORT sans the extension. */ - strcpy (ropt, "runfile="); - strcat (ropt, tail (src_short)); - for (np = ropt + strlen (ropt) - 1; np > ropt; np--) - if (*np == '.') - { - *np = '\0'; - break; - } + /* The ``dest'' may have symlinks somewhere in the path itself. */ + if (!_solve_symlinks(dest, real_dest)) + return -1; - /* `stubedit' needs its argument with the .EXE suffix explicit. */ - np = dest_abs + strlen (dest_abs); - if (np - dest_abs > 4 && stricmp (np - 4, EXE_SUFFIX) != 0) - strcat (dest_abs, EXE_SUFFIX); + /* Check if there already is file with symlink's name */ + if (__file_exists(real_dest)) + { + errno = EEXIST; + return -1; + } - /* Any file is already a link to itself. */ - if (stricmp (src_abs, dest_abs) == 0) - return 0; + symlink_file = _creat(real_dest, 0); + if (symlink_file < 0) + return -1; /* Return errno from creat() call */ + write(symlink_file, _SYMLINK_PREFIX, _SYMLINK_PREFIX_LEN); + write(symlink_file, source, strlen(source)); + write(symlink_file, "\n", 1); + write(symlink_file, fill_buf, _SYMLINK_FILE_LEN - _SYMLINK_PREFIX_LEN - strlen(source) - 1); + _close(symlink_file); - if (spawnlp (P_WAIT, STUBIFY, STUBIFY, "-g", dest_abs, (char *)0) - || spawnlp (P_WAIT, STUBEDIT, STUBEDIT, dest_abs, ropt, (char *)0)) - return -1; - return 0; + return 0; } diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/symlink.txh djgpp/src/libc/posix/unistd/symlink.txh --- djgpp.old/src/libc/posix/unistd/symlink.txh Sun Sep 27 17:22:28 1998 +++ djgpp/src/libc/posix/unistd/symlink.txh Sun Dec 12 15:20:14 1999 @@ -8,29 +8,12 @@ @end example @subheading Description -MSDOS doesn't support symbolic links. However, DJGPP supports -``symlinks'' to DJGPP programs. This function simulates a symlink -between two @file{.exe} files in the DJGPP style. It creates a program -whose name is pointed to by @var{new} which, when run, will actually -execute the program @var{exists} passing it the string pointed by -@var{new} in @code{argv[0]} (some programs change their behavior -depending on what's passed in @code{argv[0]}). The file referred to by -@var{exists} doesn't really have to exist when this function is called. -If @var{exists} points to an @emph{existing} file, the function checks -that it is a DJGPP executable; if not, the call will fail with -@code{EXDEV}. - -Both @var{new} and @var{exists} can point to a name with or without the -@file{.exe} extension. - -Note that both @var{exists} and @var{new} must specify file names which -reside in the same -directory (this is a restriction of the DJGPP ``symlinks''); the -function will fail and set @code{errno} to @code{EXDEV} if they aren't. - -This functions runs the @samp{stubify} and @samp{stubedit} programs, so -they should be somewhere on your @samp{PATH} for the function to -succeed. (These programs come with the DJGPP development distribution.) +DOS does not support symbolic links. However, DJGPP emulates them--- +this function creates a file with special size and format, so other +DJGPP library functions transparently work with file which is pointed to +by symlink. Of course, it does not work outside DJGPP programs. Those +library functions which are simple wrappers about DOS calls do not +use symlinks neither. @subheading Return Value @@ -46,3 +29,4 @@ @example symlink ("c:/djgpp/bin/grep", "c:/djgpp/bin/fgrep"); @end example + diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/xsymlink.c djgpp/src/libc/posix/unistd/xsymlink.c --- djgpp.old/src/libc/posix/unistd/xsymlink.c Thu Jan 1 00:00:00 1970 +++ djgpp/src/libc/posix/unistd/xsymlink.c Fri Dec 17 18:26:30 1999 @@ -0,0 +1,95 @@ +/* Copyright (C) 1999 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 +#include +#include +#include +#include +#include +#include + +#include "xsymlink.h" + +char _SYMLINK_PREFIX[] = "!"; + +unsigned _SYMLINK_PREFIX_LEN = sizeof(_SYMLINK_PREFIX) - 1; + +int _solve_symlinks(const char * symlink_path, char * real_path) +{ + int bytes_copied; + char * dir_sep_ptr; + int old_errno; + char fn_buf[FILENAME_MAX + 1]; + char path_copy[FILENAME_MAX + 1]; + int link_level; + int found_absolute_path; + int one_more_time = 1; + int piece_length; + + if (!symlink_path || !real_path) + { + errno = EINVAL; + return 0; + } + + if (strlen(symlink_path) > FILENAME_MAX) + { + errno = ENAMETOOLONG; + return 0; + } + + strcpy(path_copy, symlink_path); + strcpy(real_path, path_copy); + dir_sep_ptr = strpbrk(real_path, "/\\"); + while (dir_sep_ptr || one_more_time) + { + if (dir_sep_ptr) + { + *dir_sep_ptr = '\0'; + /* We have to compute it here, because real_path can change during + resolving and we get a nasty SIGSEGV... */ + piece_length = dir_sep_ptr - real_path; + } + old_errno = errno; + link_level = 0; + found_absolute_path = 0; + do + { + bytes_copied = readlink(real_path, fn_buf, FILENAME_MAX); + if (bytes_copied != -1) + { + link_level++; + fn_buf[bytes_copied] = '\0'; + strcpy(real_path, fn_buf); + /* FIXME: does absolute path check below work with chroot()? */ + if (((bytes_copied > 2) && (real_path[1] == ':')) || + ((bytes_copied > 0) && ((real_path[0] == '/') || (real_path[0] == '\\')))) + { + found_absolute_path = 1; + } + } + } while ((bytes_copied != -1) && (link_level <= _POSIX_LINK_MAX)); + if (link_level > _POSIX_LINK_MAX) + { + errno = EMLINK; + return 0; + } + else + errno = old_errno; + if (dir_sep_ptr) + strcat(real_path, path_copy + piece_length); + strcpy(path_copy, real_path); + if (dir_sep_ptr) + dir_sep_ptr = strpbrk(found_absolute_path ? + real_path : + (dir_sep_ptr + 1), "/\\"); + one_more_time = dir_sep_ptr ? 0 : !one_more_time; + } + return 1; +} + diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/xsymlink.h djgpp/src/libc/posix/unistd/xsymlink.h --- djgpp.old/src/libc/posix/unistd/xsymlink.h Thu Jan 1 00:00:00 1970 +++ djgpp/src/libc/posix/unistd/xsymlink.h Wed Dec 8 19:46:30 1999 @@ -0,0 +1,18 @@ +/* Copyright (C) 1999 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's symlinks */ +extern char _SYMLINK_PREFIX[]; /* = "!"; */ +unsigned _SYMLINK_PREFIX_LEN; +/* 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_ + diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/xsymlink.txh djgpp/src/libc/posix/unistd/xsymlink.txh --- djgpp.old/src/libc/posix/unistd/xsymlink.txh Thu Jan 1 00:00:00 1970 +++ djgpp/src/libc/posix/unistd/xsymlink.txh Sun Dec 12 15:25:16 1999 @@ -0,0 +1,36 @@ +@node _solve_symlinks, io +@subheading Syntax + +@example +#include + +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 + + _solve_symlinks(fn, file_name); + printf("Filename %s is really %s\n", fn, file_name); + + +@end example + --------------4C053DA2EC7A23114D04E754--