Date: Tue, 20 May 1997 11:52:51 +0200 (METDST) From: Robert Hoehne To: DJGPP workers Subject: Patch for symlink Message-Id: Mime-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Precedence: bulk During my port of binutils 2.8 I found an annoing thing with ln.exe. When running on W95 with long filenames enabled, the following command will success!! ln -s foo.c foo_s.c and produces a file foo_s.c.exe When running under no LFN this will produce an error and nonzero exit code of ln.exe (which is OK). To fix this I wrote a little patch for symlink.c. Please read the comments inside the patch, because the current behaviour of ln is in my opinion not correct. ln -s foo dummy is OK even when foo or foo.exe not exists. This may be a feature or not. I think it is a misfeature but I have not changed this beahaviour (I commented it only and wrote how to disable this). I have also updated the doc to reflect my changes. *** src/libc/posix/unistd/symlink.c~ Thu Sep 19 22:40:46 1996 --- src/libc/posix/unistd/symlink.c Wed May 14 21:36:52 1997 *************** *** 29,34 **** --- 29,106 ---- return p; } + /* + This code is borrowed from dosexec.c + It 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) + { + int is_stubbed = 0, is_coff = 0; + unsigned short header[3]; + int pf; + int stub_offset; + + int v2_0 = 0; + + pf = open(program, O_RDONLY|O_BINARY); + + if (pf < 0) + return -1; + + read(pf, header, sizeof(header)); + if (header[0] == 0x010b || header[0] == 0x014c) + { + unsigned char firstbytes[1]; + unsigned long coffhdr[40]; + + /* Seems to be an unstubbed COFF. See what the first opcode + is to determine if it's v1.x or v2 COFF (or an impostor). + + FIXME: the code here assumes that any COFF that's not a V1 + can only be V2. What about other compilers that use COFF? */ + is_coff = 1; + if (lseek(pf, 2, 1) < 0 + || read(pf, coffhdr, sizeof(coffhdr)) != sizeof(coffhdr) + || lseek(pf, coffhdr[10 + 5], 0) < 0 + || read(pf, firstbytes, 1) != 1) /* scnptr */ + is_coff = 0; /* "Aha! An impostor!" (The Adventure game) */ + else if (firstbytes[0] != 0xa3) /* opcode of movl %eax, 0x12345678 (V1) */ + v2_0 = 1; + } + else if (header[0] == 0x5a4d) /* "MZ" */ + { + int header_offset = (long)header[2]*512L; + char cmdline[9]; + is_stubbed = 1; + if (header[1]) + header_offset += (long)header[1] - 512L; + lseek(pf, 512, 0); + read(pf, cmdline, 8); + cmdline[8] = 0; + if (strcmp(cmdline, "go32stub") == 0) + { + v2_0 = 1; + is_coff = 1; + } + else + { + lseek(pf, header_offset - 4, 0); + read(pf, &stub_offset, 4); + header[0] = 0; + read(pf, header, sizeof(header)); + if (header[0] == 0x010b) + is_coff = 1; + if (header[0] == 0x014c) + is_coff = 1; + } + } + close(pf); + return is_coff && v2_0; + } + /* Support the DJGPP ``symlinks'' for .exe files. */ int symlink (const char *source, const char *dest) *************** *** 38,43 **** --- 110,117 ---- 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); *************** *** 54,64 **** --- 128,172 ---- 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) - 4; if (stricmp (np, EXE_SUFFIX)) + { 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); + } + + /* Comment by RH: + In my opinion here should be + if (v2_prog != 1) + which means, to allow a symlink ONLY to real existing + v2 executable. But I don't want to change too much at the + current behaviour of this function. + */ + /* 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]). */ *** src/libc/posix/unistd/symlink.tx~ Thu Sep 19 22:41:08 1996 --- src/libc/posix/unistd/symlink.txh Wed May 14 21:41:54 1997 *************** *** 23,29 **** directory (this is a restriction of the DJGPP ``symlinks''); the function will fail and set @code{errno} to @code{EXDEV} if they aren't. Also note that this function does nothing to ensure that @var{*exists} ! is actually a DJGPP program. This functions runs the @samp{stubify} and @samp{stubedit} programs, so they should be somewhere on your @samp{PATH} for the function to --- 23,31 ---- directory (this is a restriction of the DJGPP ``symlinks''); the function will fail and set @code{errno} to @code{EXDEV} if they aren't. Also note that this function does nothing to ensure that @var{*exists} ! is actually a DJGPP program whith the exception, that if the file ! @var{*exists} does really exist and it is not a usable DJGPP executable, ! this function will also fail with @code{errno} set to @code{EXDEV}. This functions runs the @samp{stubify} and @samp{stubedit} programs, so they should be somewhere on your @samp{PATH} for the function to