Mail Archives: cygwin/2013/04/03/03:18:23
X-Recipient: | archive-cygwin AT delorie DOT com
|
DomainKey-Signature: | a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id
|
| :list-unsubscribe:list-subscribe:list-archive:list-post
|
| :list-help:sender:message-id:date:from:mime-version:to:subject
|
| :content-type; q=dns; s=default; b=ldZ4FuXfnLwahAwa200w7o9lMlub8
|
| WBNTS82yGD5CyE3zFbviRyB0s/KFT3oSlw155edvndoFg2zrfDu38fx9Wi0yF/6a
|
| rZJI7EMkyBoRhqzJsO43HxAkdIsyv+LNDuNoCpzltdTlprfnzRijxyrxgKT4m5H1
|
| c9LFYHDK/a1ZsA=
|
DKIM-Signature: | v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id
|
| :list-unsubscribe:list-subscribe:list-archive:list-post
|
| :list-help:sender:message-id:date:from:mime-version:to:subject
|
| :content-type; s=default; bh=8wQ7nsoiZqXRX1sMK4Ye5G1UlNM=; b=yEO
|
| blhG6CxSIH1KWdPZ0dwCkHufqmacBTqVGNBSqPBr6i8XLB/fdErox2eSIvhwpe4B
|
| JmBywIqJR6JsCmWpbQFS3tvO49KK+if0kPb1Ec6ioGbDZrSoUXp26mzLq0swYQ39
|
| /cKFzsgB7nI+elm9RKc/prKSnsKTPIjcZh7IfO4w=
|
Mailing-List: | contact cygwin-help AT cygwin DOT com; run by ezmlm
|
List-Id: | <cygwin.cygwin.com>
|
List-Subscribe: | <mailto:cygwin-subscribe AT cygwin DOT com>
|
List-Archive: | <http://sourceware.org/ml/cygwin/>
|
List-Post: | <mailto:cygwin AT cygwin DOT com>
|
List-Help: | <mailto:cygwin-help AT cygwin DOT com>, <http://sourceware.org/ml/#faqs>
|
Sender: | cygwin-owner AT cygwin DOT com
|
Mail-Followup-To: | cygwin AT cygwin DOT com
|
Delivered-To: | mailing list cygwin AT cygwin DOT com
|
X-Spam-SWARE-Status: | No, score=-7.9 required=5.0 tests=AWL,BAYES_00,KHOP_PGP_SIGNED,RP_MATCHES_RCVD,SPF_HELO_PASS,TW_NL,TW_RG autolearn=ham version=3.3.1
|
Message-ID: | <515BD718.8080605@dancol.org>
|
Date: | Wed, 03 Apr 2013 00:15:36 -0700
|
From: | Daniel Colascione <dancol AT dancol DOT org>
|
User-Agent: | Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20130328 Thunderbird/17.0.5
|
MIME-Version: | 1.0
|
To: | cygwin AT cygwin DOT com
|
Subject: | winln for native symlinks
|
X-Virus-Found: | No
|
------enig2RBKIBRFBFOLOGAMKHTEQ
Content-Type: multipart/mixed;
boundary="------------020704010003050809020108"
This is a multi-part message in MIME format.
--------------020704010003050809020108
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
In light of the recent discussion on the developers list about native symli=
nks,
I'd like to suggest including my winln program (which I posted a while ago =
on
this list, and which I've attached to this message) in the cygutils package.
It's a drop-in replacement for GNU ld.
--------------020704010003050809020108
Content-Type: text/plain; charset=windows-1252;
name="winln.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="winln.c"
/**
* GNU ln(1) workalike that creates Windows links (hard and symbolic)
* instead of Cygwin ones.
*
* Revision History:
*
* Version 1.2 - TBD
*
* - Fix off-by-one error in to_wcs that caused it to sometimes return
* an unterminated string.
*
* Version 1.1 - 2011-12-04
*
* - Use Cygwin functions to convert between character encodings,
* correctly respecting locale.
*
* - Explain bugs worked around in the code.
*
* - Ensure that we don't create relative symlinks to invalid
* filenames.
*
* - Print message when user lacks SeCreateSymbolicLinkPrivilege and
* suggest a way to enable the privilege.
*
* Version 1.0 - 2011-04-06
*
* - Initial release
*
*/
#define _WIN32_WINNT 0x0500 /*Win2k*/
#define STRICT
#define UNICODE 1
#define _UNICODE 1
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <getopt.h>
#include <unistd.h>
#include <string.h>
#include <locale.h>
#include <errno.h>
#include <sys/cygwin.h>
#include <sys/stat.h>
#include <libgen.h>
#define PRGNAME "winln"
#define PRGVER "1.2"
#define PRGAUTHOR "Daniel Colascione <dan DOT colascione AT gmail DOT com>"
#define PRGCOPY "Copyright (C) 2011 " PRGAUTHOR
#define PRGLICENSE "GPLv2 or later <http://www.gnu.org/licenses/gpl-2.0.htm=
l>"
static BOOLEAN WINAPI
(*XCreateSymbolicLinkW)
(LPWSTR lpSymlinkFileName,
LPWSTR lpTargetFileName,
DWORD dwFlags);
static char*
to_mbs(const wchar_t* wc);
static wchar_t*
to_wcs(const char* mbs);
static void
usage()
{
fprintf(
stdout,
PRGNAME " [OPTION] TARGET LINKNAME: like ln(1) for native Windows l=
inks\n"
"\n"
" -s --symbolic: make symbolic links\n"
" -v --verbose: verbose\n"
" -f --force: replace existing links\n"
" -d --directory: always treat TARGET as a directory\n"
" -F --file: always treat TARGET as a file\n"
" -A --auto: guess type of TARGET [default]\n"
" if TARGET does not exist, treat as file\n"
"\n"
PRGNAME " -h\n"
PRGNAME " --help\n"
"\n"
" Display this help message.\n"
"\n"
PRGNAME " -V\n"
PRGNAME " --version\n"
"\n"
" Display version information.\n"
);
}
static void
versinfo ()
{
fprintf(stdout,
PRGNAME " " PRGVER "\n"
PRGCOPY "\n"
PRGLICENSE "\n"
);
}
/* Decode a Win32 error code to a localized string encoded according
to the current locale. Return a malloc()ed string. */
static char*
errmsg(DWORD errorcode)
{
wchar_t* wcsmsg =3D NULL;
char* msg =3D NULL;
FormatMessageW(
(FORMAT_MESSAGE_FROM_SYSTEM|
FORMAT_MESSAGE_ALLOCATE_BUFFER),
NULL,
errorcode,
0,
(LPWSTR)&wcsmsg,
0,
NULL);
if(wcsmsg !=3D NULL) {
msg =3D to_mbs(wcsmsg);
LocalFree(wcsmsg);
if(msg && msg[0] && msg[strlen(msg) - 1] =3D=3D '\n') {
msg[strlen(msg) - 1] =3D '\0';
}
}
if(msg =3D=3D NULL) {
msg =3D strdup("[unknown error]");
}
return msg;
}
static const struct option longopts[] =3D
{
{ "verbose", 0, 0, 'v' },
{ "directory", 0, 0, 'd' },
{ "file", 0, 0, 'F' },
{ "symbolic", 0, 0, 's' },
{ "force", 0, 0, 'f' },
{ "auto", 0, 0, 'A' },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'V' },
{ "no-target-directory", 0, 0, 'T' },
{ "target-directory", 1, 0, 't' },
{ 0 }
};
/* Output information about link on stdout */
static int verbose =3D 0;
/* Overwrite existing links */
static int force =3D 0;
/* Create symbolic links */
static int symbolic =3D 0;
/* Never treat last argument as a directory */
static int no_tgt_dir =3D 0;
enum type_mode {
MODE_FORCE_FILE,
MODE_FORCE_DIR,
MODE_AUTO,
};
static enum type_mode mode =3D MODE_AUTO;
/* Convert the given string (which is encoded in the current locale)
to a wide character string. The returned string is malloced.
Return NULL on failure. */
static wchar_t*
to_wcs(const char* mbs)
{
size_t wcs_length =3D mbstowcs(NULL, mbs, 0) + 1;
wchar_t* wcs =3D malloc(wcs_length * sizeof(*wcs));
if(wcs !=3D NULL) {
if(mbstowcs(wcs, mbs, wcs_length) =3D=3D (size_t) -1) {
free(wcs);
wcs =3D NULL;
}
}
return wcs;
}
/* Convert a wide-character string to a malloced multibyte string
encoded as specified in the current locale. Return NULL on
failure. */
static char*
to_mbs(const wchar_t* wcs)
{
size_t mbs_length =3D wcstombs(NULL, wcs, 0) + 1;
char* mbs =3D malloc(mbs_length * sizeof(*mbs));
if(mbs !=3D NULL) {
if(wcstombs(mbs, wcs, mbs_length) =3D=3D (size_t) -1) {
free(mbs);
mbs =3D NULL;
}
}
return mbs;
}
/* Convert path to Win32. If we're given an absolute path, use normal
Cygwin conversion functions. If we've given a relative path, work
around the cygwin_conv_path deficiency described below by using a
very simple filename transformation.
Return NULL on failure.
XXX: we treat relative paths specially because cygwin_create_path
fails to actually return a relative path for a reference to the
parent directory. Say we have this directory structure:
dir/foo
dir/subdir/
With CWD in dir/subdir, we run winln -sv ../foo.
cygwin_create_path will actually yield the _absolute_ path to foo,
not the correct relative Windows path, ..\foo.
*/
static wchar_t*
conv_path_to_win32(const char* posix_path)
{
wchar_t* w32_path =3D NULL;
size_t posix_path_length =3D strlen(posix_path);
if(posix_path_length < 1) {
errno =3D EINVAL;
return NULL;
}
if(posix_path[0] !=3D '/' &&
posix_path[posix_path_length - 1] !=3D '.' &&
strcspn(posix_path, "?<>\\:*|") =3D=3D posix_path_length)
{
char* tmp =3D strdup(posix_path);
char* tmp2;
for(tmp2 =3D tmp; *tmp2; ++tmp2) {
if(*tmp2 =3D=3D '/') {
*tmp2 =3D '\\';
}
}
w32_path =3D to_wcs(tmp);
free(tmp);
}
if(w32_path =3D=3D NULL) {
w32_path =3D cygwin_create_path(
CCP_POSIX_TO_WIN_W | CCP_RELATIVE, posix_path);
}
return w32_path;
}
/* Make a link. Return 0 on success, something else on error. */
static int
do_link(const char* target, const char* link)
{
/* Work around a bug that causes Cygwin to resolve the path if it
ends in a native symbolic link.
The bug is described on the Cygwin mailing list in message
<AANLkTi=3D98+M5sAsGp4vT09UN9uisqp0M=3DmgJi9WcSObG AT mail DOT gmail DOT com>..
That this bug makes symlinks-to-symlinks point to the
ultimate target, and there's no good way around that.
XXX: The workaround is here racy. The idea here is that if
we're going to overwrite the link anyway, we can just
remove the link first so that cygwin_conv_path doesn't
follow the now non-existant symlink.
*/
struct stat lstatbuf;
int lstat_success =3D 0;
struct stat statbuf;
int stat_success =3D 0;
struct stat target_statbuf;
int target_stat_success =3D 0;
wchar_t* w32link =3D NULL;
wchar_t* w32target =3D NULL;
DWORD flags;
int ret =3D 0;
if(lstat(link, &lstatbuf) =3D=3D 0) {
lstat_success =3D 1;
if(stat(link, &statbuf) =3D=3D 0) {
stat_success =3D 1;
}
if(force) {
if(unlink(link)) {
fprintf(stderr,
PRGNAME ": cannot remove `%s': %s\n",
link, strerror(errno));
ret =3D 5;
goto out;
}
} else {
fprintf(stderr,
PRGNAME ": could not create link `%s': file exists\n",
link);
ret =3D 1;
goto out;
}
}
if(stat(target, &target_statbuf) =3D=3D 0) {
target_stat_success =3D 1;
}
w32link =3D conv_path_to_win32(link);
if(w32link =3D=3D NULL) {
fprintf(stderr, PRGNAME ": could not convert `%s' to win32 path\n",
link);
ret =3D 2;
goto out;
}
w32target =3D conv_path_to_win32(target);
if(w32target =3D=3D NULL) {
fprintf(stderr, PRGNAME ": could not convert `%s' to win32 path\n",
target);
ret =3D 2;
goto out;
}
switch(mode)
{
case MODE_FORCE_DIR:
flags =3D SYMBOLIC_LINK_FLAG_DIRECTORY;
break;
case MODE_FORCE_FILE:
flags =3D 0;
break;
default:
flags =3D 0;
if(target_stat_success && S_ISDIR(target_statbuf.st_mode)) {
flags |=3D SYMBOLIC_LINK_FLAG_DIRECTORY;
}
break;
}
/* Don't call link(2), even for hard links: we want to maintain
* absolute parity between the hard and symbolic links made using
* this tool. We don't want link targets to change just because
* we change the link type. */
if(symbolic) {
if(XCreateSymbolicLinkW(w32link, w32target, flags)) {
if(verbose) {
printf("`%s' -> `%s' [%s]\n", link, target,
flags ? "dir" : "file");
}
} else {
fprintf(stderr, PRGNAME ": failed to create symbolic link `%s':=
%s\n",
link, errmsg(GetLastError()));
ret =3D 2;
goto out;
}
} else {
if(CreateHardLinkW(w32link, w32target, 0)) {
if(verbose) {
printf("`%s' =3D> `%s'\n", link, target);
}
} else {
fprintf(stderr, PRGNAME ": failed to create hard link `%s': %s\=
n",
link, errmsg(GetLastError()));
ret =3D 2;
goto out;
}
}
out:
free(w32link);
free(w32target);
return ret;
}
static int
is_dir(const char* path)
{
struct stat statbuf;
return stat(path, &statbuf) =3D=3D 0 &&
S_ISDIR(statbuf.st_mode);
}
static BOOL
set_privilege_status (
const wchar_t* privname,
BOOL bEnablePrivilege)
{
/* After the MSDN example. */
TOKEN_PRIVILEGES tp;
LUID luid;
HANDLE hToken;
BOOL success;
hToken =3D NULL;
success =3D FALSE;
if (!OpenProcessToken (GetCurrentProcess (),
(TOKEN_QUERY |
TOKEN_ADJUST_PRIVILEGES),
&hToken))
{
goto out;
}
if ( !LookupPrivilegeValue (
NULL, // lookup privilege on local system
privname, // privilege to lookup
&luid ) ) // receives LUID of privilege
{
goto out;
}
tp.PrivilegeCount =3D 1;
tp.Privileges[0].Luid =3D luid;
if (bEnablePrivilege) {
tp.Privileges[0].Attributes =3D SE_PRIVILEGE_ENABLED;
} else {
tp.Privileges[0].Attributes =3D 0;
}
// Enable the privilege or disable all privileges.
if ( !AdjustTokenPrivileges (
hToken,
FALSE,
&tp,
sizeof (TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
goto out;
}
if (GetLastError () =3D=3D ERROR_NOT_ALL_ASSIGNED) {
goto out;
}
success =3D TRUE;
out:
if (hToken) {
CloseHandle (hToken);
}
return success;
}
int
main(int argc, char* argv[])
{
int c;
char* tgt_dir =3D NULL;
int ret =3D 0;
setlocale(LC_ALL, "");
to_mbs(L"");
to_wcs("");
while ((c =3D getopt_long(argc, argv, "VvdfFsATt:", longopts, 0)) !=3D =
-1) {
switch(c) {
case 'v':
verbose =3D 1;
break;
case 'd':
mode =3D MODE_FORCE_DIR;
break;
case 'f':
force =3D 1;
break;
case 'F':
mode =3D MODE_FORCE_FILE;
break;
case 's':
symbolic =3D 1;
break;
case 'A':
mode =3D MODE_AUTO;
break;
case 'T':
no_tgt_dir =3D 1;
break;
case 't':
tgt_dir =3D strdup(optarg);
break;
case 'h':
usage();
ret =3D 0;
goto out;
case 'V':
versinfo ();
ret =3D 0;
goto out;
default:
fprintf(stderr, PRGNAME ": use --help for usage\n");
ret =3D 4;
goto out;
}
}
if(symbolic) {
HMODULE hKernel32 =3D LoadLibraryW(L"kernel32");
if(hKernel32 =3D=3D NULL) {
fprintf(stderr, PRGNAME ": could not load kernel32: %s\n",
errmsg(GetLastError()));
ret =3D 1;
goto out;
}
XCreateSymbolicLinkW =3D
(void*)GetProcAddress(hKernel32, "CreateSymbolicLinkW");
if(XCreateSymbolicLinkW =3D=3D NULL) {
fprintf(stderr, PRGNAME ": symbolic links not supported on this=
OS\n");
ret =3D 2;
goto out;
}
if(!set_privilege_status(L"SeCreateSymbolicLinkPrivilege", TRUE)) {
fprintf(stderr,
PRGNAME ": you don't permission to create symbolic link=
s. Run,"
" as administrator,\n"
PRGNAME ": editrights -a SeCreateSymbolicLinkPrivileg=
e -a $YOUR_USER\n"
);
ret =3D 3;
goto out;
}
}
argc -=3D optind;
argv +=3D optind;
if(argc =3D=3D 0) {
fprintf(stderr, PRGNAME ": no arguments. Use --help for usage\n");
ret =3D 1;
goto out;
}
if(no_tgt_dir) {
if(argc !=3D 2) {
fprintf(stderr, PRGNAME ": must have exactly two args with -T\n=
");
ret =3D 1;
goto out;
}
ret =3D do_link(argv[0], argv[1]);
goto out;
}
if(tgt_dir =3D=3D NULL && argc =3D=3D 1) {
tgt_dir =3D ".";
}
if(tgt_dir =3D=3D NULL) {
int last_is_dir =3D is_dir(argv[argc - 1]);
if(argc =3D=3D 2 && !last_is_dir) {
ret =3D do_link(argv[0], argv[1]);
goto out;
}
if(!last_is_dir) {
fprintf(stderr, PRGNAME ": `%s': not a directory\n",
argv[argc - 1]);
ret =3D 1;
goto out;
}
tgt_dir =3D argv[--argc];
argv[argc] =3D NULL;
}
for(; *argv; ++argv) {
char* tgt;
int r;
if(asprintf(&tgt, "%s/%s", tgt_dir, basename(*argv)) =3D=3D -1) {
fprintf(stderr, PRGNAME ": asprintf: %s\n",
strerror(errno));
ret =3D 1;
goto out;
}
r =3D do_link(*argv, tgt);
if(r && ret =3D=3D 0) {
ret =3D r;
}
free(tgt);
}
out:
return ret;
}
--------------020704010003050809020108
Content-Type: text/plain; charset=windows-1252;
name="Makefile"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="Makefile"
winln: winln.c
$(CC) -Wall -Os -g -o $@ $^
clean:
rm -f winln.exe
--------------020704010003050809020108--
------enig2RBKIBRFBFOLOGAMKHTEQ
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.13 (Cygwin)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/
iEYEARECAAYFAlFb1xgACgkQ17c2LVA10Vuj0ACfW6LhyccNLoyPc9if/wWqjSu7
j2UAn3aOqZz2OH8bHrT9yxwr/KVq5AkU
=/2C4
-----END PGP SIGNATURE-----
------enig2RBKIBRFBFOLOGAMKHTEQ--
- Raw text -