Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
git2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
git2
Commits
65477db1
Commit
65477db1
authored
Apr 21, 2014
by
Edward Thomson
Committed by
Philip Kelley
Apr 22, 2014
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Handle win32 reparse points properly
parent
c2c81615
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
967 additions
and
131 deletions
+967
-131
src/win32/posix.h
+1
-1
src/win32/posix_w32.c
+201
-130
src/win32/reparse.h
+58
-0
src/win32/w32_util.c
+71
-0
src/win32/w32_util.h
+23
-0
tests/clar_libgit2.h
+11
-0
tests/core/link.c
+602
-0
No files found.
src/win32/posix.h
View file @
65477db1
...
...
@@ -30,7 +30,7 @@ GIT_INLINE(int) p_link(const char *old, const char *new)
extern
int
p_mkdir
(
const
char
*
path
,
mode_t
mode
);
extern
int
p_unlink
(
const
char
*
path
);
extern
int
p_lstat
(
const
char
*
file_name
,
struct
stat
*
buf
);
extern
int
p_readlink
(
const
char
*
link
,
char
*
target
,
size_t
target_len
);
extern
int
p_readlink
(
const
char
*
path
,
char
*
buf
,
size_t
bufsiz
);
extern
int
p_symlink
(
const
char
*
old
,
const
char
*
new
);
extern
char
*
p_realpath
(
const
char
*
orig_path
,
char
*
buffer
);
extern
int
p_vsnprintf
(
char
*
buffer
,
size_t
count
,
const
char
*
format
,
va_list
argptr
);
...
...
src/win32/posix_w32.c
View file @
65477db1
...
...
@@ -9,12 +9,13 @@
#include "path.h"
#include "utf-conv.h"
#include "repository.h"
#include "reparse.h"
#include <errno.h>
#include <io.h>
#include <fcntl.h>
#include <ws2tcpip.h>
#if
defined(__MINGW32__)
#if
ndef FILE_NAME_NORMALIZED
# define FILE_NAME_NORMALIZED 0
#endif
...
...
@@ -100,29 +101,79 @@ GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft)
return
(
time_t
)
winTime
;
}
#define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
static
int
do_lstat
(
const
char
*
file_name
,
struct
stat
*
buf
,
int
posix_enotdir
)
/* On success, returns the length, in characters, of the path stored in dest.
* On failure, returns a negative value. */
static
int
readlink_w
(
git_win32_path
dest
,
const
git_win32_path
path
)
{
WIN32_FILE_ATTRIBUTE_DATA
fdata
;
git_win32_path
fbuf
;
wchar_t
lastch
;
int
flen
;
BYTE
buf
[
MAXIMUM_REPARSE_DATA_BUFFER_SIZE
];
GIT_REPARSE_DATA_BUFFER
*
reparse_buf
=
(
GIT_REPARSE_DATA_BUFFER
*
)
buf
;
HANDLE
handle
=
NULL
;
DWORD
ioctl_ret
;
wchar_t
*
target
;
size_t
target_len
;
int
error
=
-
1
;
if
((
flen
=
utf8_to_16_with_errno
(
fbuf
,
file_name
))
<
0
)
handle
=
CreateFileW
(
path
,
GENERIC_READ
,
FILE_SHARE_READ
|
FILE_SHARE_DELETE
,
NULL
,
OPEN_EXISTING
,
FILE_FLAG_OPEN_REPARSE_POINT
|
FILE_FLAG_BACKUP_SEMANTICS
,
NULL
);
if
(
INVALID_HANDLE_VALUE
==
handle
)
{
errno
=
ENOENT
;
return
-
1
;
}
if
(
!
DeviceIoControl
(
handle
,
FSCTL_GET_REPARSE_POINT
,
NULL
,
0
,
reparse_buf
,
sizeof
(
buf
),
&
ioctl_ret
,
NULL
))
{
errno
=
EINVAL
;
goto
on_error
;
}
/* truncate trailing slashes */
for
(;
flen
>
0
;
--
flen
)
{
lastch
=
fbuf
[
flen
-
1
];
if
(
WIN32_IS_WSEP
(
lastch
))
fbuf
[
flen
-
1
]
=
L'\0'
;
else
if
(
lastch
!=
L'\0'
)
switch
(
reparse_buf
->
ReparseTag
)
{
case
IO_REPARSE_TAG_SYMLINK
:
target
=
reparse_buf
->
SymbolicLinkReparseBuffer
.
PathBuffer
+
(
reparse_buf
->
SymbolicLinkReparseBuffer
.
SubstituteNameOffset
/
sizeof
(
WCHAR
));
target_len
=
reparse_buf
->
SymbolicLinkReparseBuffer
.
SubstituteNameLength
/
sizeof
(
WCHAR
);
break
;
case
IO_REPARSE_TAG_MOUNT_POINT
:
target
=
reparse_buf
->
MountPointReparseBuffer
.
PathBuffer
+
(
reparse_buf
->
MountPointReparseBuffer
.
SubstituteNameOffset
/
sizeof
(
WCHAR
));
target_len
=
reparse_buf
->
MountPointReparseBuffer
.
SubstituteNameLength
/
sizeof
(
WCHAR
);
break
;
default
:
errno
=
EINVAL
;
goto
on_error
;
}
if
(
GetFileAttributesExW
(
fbuf
,
GetFileExInfoStandard
,
&
fdata
))
{
if
(
target_len
)
{
/* The path may need to have a prefix removed. */
target_len
=
git_win32__to_dos
(
target
,
target_len
);
/* Need one additional character in the target buffer
* for the terminating NULL. */
if
(
GIT_WIN_PATH_UTF16
>
target_len
)
{
wcscpy
(
dest
,
target
);
error
=
(
int
)
target_len
;
}
}
on_error
:
CloseHandle
(
handle
);
return
error
;
}
#define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
static
int
lstat_w
(
wchar_t
*
path
,
struct
stat
*
buf
,
bool
posix_enotdir
)
{
WIN32_FILE_ATTRIBUTE_DATA
fdata
;
if
(
GetFileAttributesExW
(
path
,
GetFileExInfoStandard
,
&
fdata
))
{
int
fMode
=
S_IREAD
;
if
(
!
buf
)
...
...
@@ -136,12 +187,6 @@ static int do_lstat(
if
(
!
(
fdata
.
dwFileAttributes
&
FILE_ATTRIBUTE_READONLY
))
fMode
|=
S_IWRITE
;
if
(
fdata
.
dwFileAttributes
&
FILE_ATTRIBUTE_REPARSE_POINT
)
fMode
|=
S_IFLNK
;
if
((
fMode
&
(
S_IFDIR
|
S_IFLNK
))
==
(
S_IFDIR
|
S_IFLNK
))
// junction
fMode
^=
S_IFLNK
;
buf
->
st_ino
=
0
;
buf
->
st_gid
=
0
;
buf
->
st_uid
=
0
;
...
...
@@ -153,16 +198,17 @@ static int do_lstat(
buf
->
st_mtime
=
filetime_to_time_t
(
&
(
fdata
.
ftLastWriteTime
));
buf
->
st_ctime
=
filetime_to_time_t
(
&
(
fdata
.
ftCreationTime
));
/* Windows symlinks have zero file size, call readlink to determine
* the length of the path pointed to, which we expect everywhere else
*/
if
(
S_ISLNK
(
fMode
))
{
git_win32_utf8_path
target
;
if
(
fdata
.
dwFileAttributes
&
FILE_ATTRIBUTE_REPARSE_POINT
)
{
git_win32_path
target
;
if
(
p_readlink
(
file_name
,
target
,
GIT_WIN_PATH_UTF8
)
==
-
1
)
return
-
1
;
if
(
readlink_w
(
target
,
path
)
>=
0
)
{
buf
->
st_mode
=
(
buf
->
st_mode
&
~
S_IFMT
)
|
S_IFLNK
;
buf
->
st_size
=
strlen
(
target
);
/* st_size gets the UTF-8 length of the target name, in bytes,
* not counting the NULL terminator */
if
((
buf
->
st_size
=
git__utf16_to_8
(
NULL
,
0
,
target
))
<
0
)
return
-
1
;
}
}
return
0
;
...
...
@@ -174,18 +220,23 @@ static int do_lstat(
* file path is a regular file, otherwise set ENOENT.
*/
if
(
posix_enotdir
)
{
size_t
path_len
=
wcslen
(
path
);
/* scan up path until we find an existing item */
while
(
1
)
{
DWORD
attrs
;
/* remove last directory component */
for
(
--
flen
;
flen
>
0
&&
!
WIN32_IS_WSEP
(
fbuf
[
flen
]);
--
flen
);
for
(
path_len
--
;
path_len
>
0
&&
!
WIN32_IS_WSEP
(
path
[
path_len
]);
path_len
--
);
if
(
f
len
<=
0
)
if
(
path_
len
<=
0
)
break
;
fbuf
[
flen
]
=
L'\0'
;
path
[
path_len
]
=
L'\0'
;
attrs
=
GetFileAttributesW
(
path
);
if
(
GetFileAttributesExW
(
fbuf
,
GetFileExInfoStandard
,
&
fdata
)
)
{
if
(
!
(
fdata
.
dwFileAttribute
s
&
FILE_ATTRIBUTE_DIRECTORY
))
if
(
INVALID_FILE_ATTRIBUTES
!=
attrs
)
{
if
(
!
(
attr
s
&
FILE_ATTRIBUTE_DIRECTORY
))
errno
=
ENOTDIR
;
break
;
}
...
...
@@ -195,105 +246,51 @@ static int do_lstat(
return
-
1
;
}
int
p_lstat
(
const
char
*
filename
,
struct
stat
*
buf
)
static
int
do_lstat
(
const
char
*
path
,
struct
stat
*
buf
,
bool
posixly_correct
)
{
return
do_lstat
(
filename
,
buf
,
0
);
git_win32_path
path_w
;
int
len
;
if
((
len
=
utf8_to_16_with_errno
(
path_w
,
path
))
<
0
)
return
-
1
;
git_win32__path_trim_end
(
path_w
,
len
);
return
lstat_w
(
path_w
,
buf
,
posixly_correct
);
}
int
p_lstat
_posixly
(
const
char
*
filename
,
struct
stat
*
buf
)
int
p_lstat
(
const
char
*
filename
,
struct
stat
*
buf
)
{
return
do_lstat
(
filename
,
buf
,
1
);
return
do_lstat
(
filename
,
buf
,
false
);
}
/*
* Returns the address of the GetFinalPathNameByHandleW function.
* This function is available on Windows Vista and higher.
*/
static
PFGetFinalPathNameByHandleW
get_fpnbyhandle
(
void
)
int
p_lstat_posixly
(
const
char
*
filename
,
struct
stat
*
buf
)
{
static
PFGetFinalPathNameByHandleW
pFunc
=
NULL
;
PFGetFinalPathNameByHandleW
toReturn
=
pFunc
;
if
(
!
toReturn
)
{
HMODULE
hModule
=
GetModuleHandleW
(
L"kernel32"
);
if
(
hModule
)
toReturn
=
(
PFGetFinalPathNameByHandleW
)
GetProcAddress
(
hModule
,
"GetFinalPathNameByHandleW"
);
pFunc
=
toReturn
;
}
assert
(
toReturn
);
return
toReturn
;
return
do_lstat
(
filename
,
buf
,
true
);
}
/*
* Parts of the The p_readlink function are heavily inspired by the php
* readlink function in link_win32.c
*
* Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
*
* For details of the PHP license see http://www.php.net/license/3_01.txt
*/
int
p_readlink
(
const
char
*
link
,
char
*
target
,
size_t
target_len
)
int
p_readlink
(
const
char
*
path
,
char
*
buf
,
size_t
bufsiz
)
{
static
const
wchar_t
prefix
[]
=
L"
\\\\
?
\\
"
;
PFGetFinalPathNameByHandleW
pgfp
=
get_fpnbyhandle
();
HANDLE
hFile
=
NULL
;
wchar_t
*
target_w
=
NULL
;
bool
trim_prefix
;
git_win32_path
link_w
;
DWORD
dwChars
,
dwLastError
;
int
error
=
-
1
;
/* Check that we found the function, and convert to UTF-16 */
if
(
!
pgfp
||
utf8_to_16_with_errno
(
link_w
,
link
)
<
0
)
goto
on_error
;
/* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
* specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
* target of the link. */
hFile
=
CreateFileW
(
link_w
,
GENERIC_READ
,
FILE_SHARE_READ
|
FILE_SHARE_DELETE
,
NULL
,
OPEN_EXISTING
,
FILE_FLAG_BACKUP_SEMANTICS
,
NULL
);
if
(
hFile
==
INVALID_HANDLE_VALUE
)
goto
on_error
;
/* Find out how large the buffer should be to hold the result */
if
(
!
(
dwChars
=
pgfp
(
hFile
,
NULL
,
0
,
FILE_NAME_NORMALIZED
)))
goto
on_error
;
if
(
!
(
target_w
=
git__malloc
(
dwChars
*
sizeof
(
wchar_t
))))
goto
on_error
;
/* Call a second time */
dwChars
=
pgfp
(
hFile
,
target_w
,
dwChars
,
FILE_NAME_NORMALIZED
);
if
(
!
dwChars
)
goto
on_error
;
/* Do we need to trim off a \\?\ from the start of the path? */
trim_prefix
=
(
dwChars
>=
ARRAY_SIZE
(
prefix
))
&&
!
wcsncmp
(
prefix
,
target_w
,
ARRAY_SIZE
(
prefix
));
/* Convert the result to UTF-8 */
if
(
git__utf16_to_8
(
target
,
target_len
,
trim_prefix
?
target_w
+
4
:
target_w
)
<
0
)
goto
on_error
;
error
=
0
;
git_win32_path
path_w
,
target_w
;
git_win32_utf8_path
target
;
int
len
;
on_error
:
dwLastError
=
GetLastError
();
/* readlink(2) does not NULL-terminate the string written
* to the target buffer. Furthermore, the target buffer need
* not be large enough to hold the entire result. A truncated
* result should be written in this case. Since this truncation
* could occur in the middle of the encoding of a code point,
* we need to buffer the result on the stack. */
if
(
hFile
&&
INVALID_HANDLE_VALUE
!=
hFile
)
CloseHandle
(
hFile
);
if
(
utf8_to_16_with_errno
(
path_w
,
path
)
<
0
||
readlink_w
(
target_w
,
path_w
)
<
0
||
(
len
=
git_win32_path_to_utf8
(
target
,
target_w
))
<
0
)
return
-
1
;
if
(
target_w
)
git__free
(
target_w
);
bufsiz
=
min
((
size_t
)
len
,
bufsiz
);
memcpy
(
buf
,
target
,
bufsiz
);
SetLastError
(
dwLastError
);
return
error
;
return
(
int
)
bufsiz
;
}
int
p_symlink
(
const
char
*
old
,
const
char
*
new
)
...
...
@@ -356,20 +353,94 @@ int p_getcwd(char *buffer_out, size_t size)
return
0
;
}
/*
* Returns the address of the GetFinalPathNameByHandleW function.
* This function is available on Windows Vista and higher.
*/
static
PFGetFinalPathNameByHandleW
get_fpnbyhandle
(
void
)
{
static
PFGetFinalPathNameByHandleW
pFunc
=
NULL
;
PFGetFinalPathNameByHandleW
toReturn
=
pFunc
;
if
(
!
toReturn
)
{
HMODULE
hModule
=
GetModuleHandleW
(
L"kernel32"
);
if
(
hModule
)
toReturn
=
(
PFGetFinalPathNameByHandleW
)
GetProcAddress
(
hModule
,
"GetFinalPathNameByHandleW"
);
pFunc
=
toReturn
;
}
assert
(
toReturn
);
return
toReturn
;
}
static
int
getfinalpath_w
(
git_win32_path
dest
,
const
wchar_t
*
path
)
{
PFGetFinalPathNameByHandleW
pgfp
=
get_fpnbyhandle
();
HANDLE
hFile
;
DWORD
dwChars
;
if
(
!
pgfp
)
return
-
1
;
/* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
* specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
* target of the link. */
hFile
=
CreateFileW
(
path
,
GENERIC_READ
,
FILE_SHARE_READ
|
FILE_SHARE_DELETE
,
NULL
,
OPEN_EXISTING
,
FILE_FLAG_BACKUP_SEMANTICS
,
NULL
);
if
(
hFile
==
INVALID_HANDLE_VALUE
)
return
-
1
;
/* Call GetFinalPathNameByHandle */
dwChars
=
pgfp
(
hFile
,
dest
,
GIT_WIN_PATH_UTF16
,
FILE_NAME_NORMALIZED
);
if
(
!
dwChars
||
dwChars
>=
GIT_WIN_PATH_UTF16
)
{
DWORD
error
=
GetLastError
();
CloseHandle
(
hFile
);
SetLastError
(
error
);
return
-
1
;
}
CloseHandle
(
hFile
);
/* The path may be delivered to us with a prefix; canonicalize */
return
(
int
)
git_win32__to_dos
(
dest
,
dwChars
);
}
static
int
follow_and_lstat_link
(
git_win32_path
path
,
struct
stat
*
buf
)
{
git_win32_path
target_w
;
if
(
getfinalpath_w
(
target_w
,
path
)
<
0
)
return
-
1
;
return
lstat_w
(
target_w
,
buf
,
false
);
}
int
p_stat
(
const
char
*
path
,
struct
stat
*
buf
)
{
git_win32_
utf8_path
target
;
int
error
=
0
;
git_win32_
path
path_w
;
int
len
;
error
=
do_lstat
(
path
,
buf
,
0
);
if
((
len
=
utf8_to_16_with_errno
(
path_w
,
path
))
<
0
)
return
-
1
;
/* We need not do this in a loop to unwind chains of symlinks since
* p_readlink calls GetFinalPathNameByHandle which does it for us. */
if
(
error
>=
0
&&
S_ISLNK
(
buf
->
st_mode
)
&&
(
error
=
p_readlink
(
path
,
target
,
GIT_WIN_PATH_UTF8
))
>=
0
)
error
=
do_lstat
(
target
,
buf
,
0
);
git_win32__path_trim_end
(
path_w
,
len
);
return
error
;
if
(
lstat_w
(
path_w
,
buf
,
false
)
<
0
)
return
-
1
;
/* The item is a symbolic link or mount point. No need to iterate
* to follow multiple links; use GetFinalPathNameFromHandle. */
if
(
S_ISLNK
(
buf
->
st_mode
))
return
follow_and_lstat_link
(
path_w
,
buf
);
return
0
;
}
int
p_chdir
(
const
char
*
path
)
...
...
src/win32/reparse.h
0 → 100644
View file @
65477db1
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_win32_reparse_h__
#define INCLUDE_git_win32_reparse_h__
/* This structure is defined on MSDN at
* http://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
*
* It was formerly included in the Windows 2000 SDK and remains defined in
* MinGW, so we must define it with a silly name to avoid conflicting.
*/
typedef
struct
_GIT_REPARSE_DATA_BUFFER
{
ULONG
ReparseTag
;
USHORT
ReparseDataLength
;
USHORT
Reserved
;
union
{
struct
{
USHORT
SubstituteNameOffset
;
USHORT
SubstituteNameLength
;
USHORT
PrintNameOffset
;
USHORT
PrintNameLength
;
ULONG
Flags
;
WCHAR
PathBuffer
[
1
];
}
SymbolicLinkReparseBuffer
;
struct
{
USHORT
SubstituteNameOffset
;
USHORT
SubstituteNameLength
;
USHORT
PrintNameOffset
;
USHORT
PrintNameLength
;
WCHAR
PathBuffer
[
1
];
}
MountPointReparseBuffer
;
struct
{
UCHAR
DataBuffer
[
1
];
}
GenericReparseBuffer
;
};
}
GIT_REPARSE_DATA_BUFFER
;
#define REPARSE_DATA_HEADER_SIZE 8
#define REPARSE_DATA_MOUNTPOINT_HEADER_SIZE 8
#define REPARSE_DATA_UNION_SIZE 12
/* Missing in MinGW */
#ifndef FSCTL_GET_REPARSE_POINT
# define FSCTL_GET_REPARSE_POINT 0x000900a8
#endif
/* Missing in MinGW */
#ifndef FSCTL_SET_REPARSE_POINT
# define FSCTL_SET_REPARSE_POINT 0x000900a4
#endif
#endif
\ No newline at end of file
src/win32/w32_util.c
View file @
65477db1
...
...
@@ -7,6 +7,8 @@
#include "w32_util.h"
#define CONST_STRLEN(x) ((sizeof(x)/sizeof(x[0])) - 1)
/**
* Creates a FindFirstFile(Ex) filter string from a UTF-8 path.
* The filter string enumerates all items in the directory.
...
...
@@ -67,3 +69,71 @@ int git_win32__sethidden(const char *path)
return
0
;
}
/**
* Removes any trailing backslashes from a path, except in the case of a drive
* letter path (C:\, D:\, etc.). This function cannot fail.
*
* @param path The path which should be trimmed.
* @return The length of the modified string (<= the input length)
*/
size_t
git_win32__path_trim_end
(
wchar_t
*
str
,
size_t
len
)
{
while
(
1
)
{
if
(
!
len
||
str
[
len
-
1
]
!=
L'\\'
)
break
;
/* Don't trim backslashes from drive letter paths, which
* are 3 characters long and of the form C:\, D:\, etc. */
if
(
3
==
len
&&
git_win32__isalpha
(
str
[
0
])
&&
str
[
1
]
==
':'
)
break
;
len
--
;
}
str
[
len
]
=
L'\0'
;
return
len
;
}
/**
* Removes any of the following namespace prefixes from a path,
* if found: "\??\", "\\?\", "\\?\UNC\". This function cannot fail.
*
* @param path The path which should be converted.
* @return The length of the modified string (<= the input length)
*/
size_t
git_win32__to_dos
(
wchar_t
*
str
,
size_t
len
)
{
static
const
wchar_t
dosdevices_prefix
[]
=
L"
\\
\?\?
\\
"
;
static
const
wchar_t
nt_prefix
[]
=
L"
\\\\
?
\\
"
;
static
const
wchar_t
unc_prefix
[]
=
L"UNC
\\
"
;
size_t
to_advance
=
0
;
/* "\??\" -- DOS Devices prefix */
if
(
len
>=
CONST_STRLEN
(
dosdevices_prefix
)
&&
!
wcsncmp
(
str
,
dosdevices_prefix
,
CONST_STRLEN
(
dosdevices_prefix
)))
{
to_advance
+=
CONST_STRLEN
(
dosdevices_prefix
);
len
-=
CONST_STRLEN
(
dosdevices_prefix
);
}
/* "\\?\" -- NT namespace prefix */
else
if
(
len
>=
CONST_STRLEN
(
nt_prefix
)
&&
!
wcsncmp
(
str
,
nt_prefix
,
CONST_STRLEN
(
nt_prefix
)))
{
to_advance
+=
CONST_STRLEN
(
nt_prefix
);
len
-=
CONST_STRLEN
(
nt_prefix
);
}
/* "\??\UNC\", "\\?\UNC\" -- UNC prefix */
if
(
to_advance
&&
len
>=
CONST_STRLEN
(
unc_prefix
)
&&
!
wcsncmp
(
str
+
to_advance
,
unc_prefix
,
CONST_STRLEN
(
unc_prefix
)))
{
to_advance
+=
CONST_STRLEN
(
unc_prefix
);
len
-=
CONST_STRLEN
(
unc_prefix
);
}
if
(
to_advance
)
{
memmove
(
str
,
str
+
to_advance
,
len
*
sizeof
(
wchar_t
));
str
[
len
]
=
L'\0'
;
}
return
git_win32__path_trim_end
(
str
,
len
);
}
\ No newline at end of file
src/win32/w32_util.h
View file @
65477db1
...
...
@@ -10,6 +10,11 @@
#include "utf-conv.h"
GIT_INLINE
(
bool
)
git_win32__isalpha
(
wchar_t
c
)
{
return
((
c
>=
L'A'
&&
c
<=
L'Z'
)
||
(
c
>=
L'a'
&&
c
<=
L'z'
));
}
/**
* Creates a FindFirstFile(Ex) filter string from a UTF-8 path.
* The filter string enumerates all items in the directory.
...
...
@@ -28,4 +33,22 @@ bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src);
*/
int
git_win32__sethidden
(
const
char
*
path
);
/**
* Removes any trailing backslashes from a path, except in the case of a drive
* letter path (C:\, D:\, etc.). This function cannot fail.
*
* @param path The path which should be trimmed.
* @return The length of the modified string (<= the input length)
*/
size_t
git_win32__path_trim_end
(
wchar_t
*
str
,
size_t
len
);
/**
* Removes any of the following namespace prefixes from a path,
* if found: "\??\", "\\?\", "\\?\UNC\". This function cannot fail.
*
* @param path The path which should be converted.
* @return The length of the modified string (<= the input length)
*/
size_t
git_win32__to_dos
(
wchar_t
*
str
,
size_t
len
);
#endif
tests/clar_libgit2.h
View file @
65477db1
...
...
@@ -29,6 +29,17 @@
#define cl_git_fail_with(expr, error) cl_assert_equal_i(error,expr)
/**
* Like cl_git_pass, only for Win32 error code conventions
*/
#define cl_win32_pass(expr) do { \
int _win32_res; \
if ((_win32_res = (expr)) == 0) { \
giterr_set(GITERR_OS, "Returned: %d, system error code: %d", _win32_res, GetLastError()); \
cl_git_report_failure(_win32_res, __FILE__, __LINE__, "System call failed: " #expr); \
} \
} while(0)
void
cl_git_report_failure
(
int
,
const
char
*
,
int
,
const
char
*
);
#define cl_assert_at_line(expr,file,line) \
...
...
tests/core/link.c
0 → 100644
View file @
65477db1
#include "clar_libgit2.h"
#include "posix.h"
#include "buffer.h"
#include "path.h"
#ifdef GIT_WIN32
# include "win32/reparse.h"
#endif
void
test_core_link__cleanup
(
void
)
{
#ifdef GIT_WIN32
RemoveDirectory
(
"lstat_junction"
);
RemoveDirectory
(
"lstat_dangling"
);
RemoveDirectory
(
"lstat_dangling_dir"
);
RemoveDirectory
(
"lstat_dangling_junction"
);
RemoveDirectory
(
"stat_junction"
);
RemoveDirectory
(
"stat_dangling"
);
RemoveDirectory
(
"stat_dangling_dir"
);
RemoveDirectory
(
"stat_dangling_junction"
);
#endif
}
#ifdef GIT_WIN32
static
bool
is_administrator
(
void
)
{
static
SID_IDENTIFIER_AUTHORITY
authority
=
{
SECURITY_NT_AUTHORITY
};
PSID
admin_sid
;
BOOL
is_admin
;
cl_win32_pass
(
AllocateAndInitializeSid
(
&
authority
,
2
,
SECURITY_BUILTIN_DOMAIN_RID
,
DOMAIN_ALIAS_RID_ADMINS
,
0
,
0
,
0
,
0
,
0
,
0
,
&
admin_sid
));
cl_win32_pass
(
CheckTokenMembership
(
NULL
,
admin_sid
,
&
is_admin
));
FreeSid
(
admin_sid
);
return
is_admin
?
true
:
false
;
}
#endif
static
void
do_symlink
(
const
char
*
old
,
const
char
*
new
,
int
is_dir
)
{
#ifndef GIT_WIN32
GIT_UNUSED
(
is_dir
);
cl_must_pass
(
symlink
(
old
,
new
));
#else
typedef
DWORD
(
WINAPI
*
create_symlink_func
)(
LPCTSTR
,
LPCTSTR
,
DWORD
);
HMODULE
module
;
create_symlink_func
pCreateSymbolicLink
;
if
(
!
is_administrator
())
clar__skip
();
cl_assert
(
module
=
GetModuleHandle
(
"kernel32"
));
cl_assert
(
pCreateSymbolicLink
=
(
create_symlink_func
)
GetProcAddress
(
module
,
"CreateSymbolicLinkA"
));
cl_win32_pass
(
pCreateSymbolicLink
(
new
,
old
,
is_dir
));
#endif
}
static
void
do_hardlink
(
const
char
*
old
,
const
char
*
new
)
{
#ifndef GIT_WIN32
cl_must_pass
(
link
(
old
,
new
));
#else
typedef
DWORD
(
WINAPI
*
create_hardlink_func
)(
LPCTSTR
,
LPCTSTR
,
LPSECURITY_ATTRIBUTES
);
HMODULE
module
;
create_hardlink_func
pCreateHardLink
;
if
(
!
is_administrator
())
clar__skip
();
cl_assert
(
module
=
GetModuleHandle
(
"kernel32"
));
cl_assert
(
pCreateHardLink
=
(
create_hardlink_func
)
GetProcAddress
(
module
,
"CreateHardLinkA"
));
cl_win32_pass
(
pCreateHardLink
(
new
,
old
,
0
));
#endif
}
#ifdef GIT_WIN32
static
void
do_junction
(
const
char
*
old
,
const
char
*
new
)
{
GIT_REPARSE_DATA_BUFFER
*
reparse_buf
;
HANDLE
handle
;
git_buf
unparsed_buf
=
GIT_BUF_INIT
;
wchar_t
*
subst_utf16
,
*
print_utf16
;
DWORD
ioctl_ret
;
int
subst_utf16_len
,
subst_byte_len
,
print_utf16_len
,
print_byte_len
,
ret
;
USHORT
reparse_buflen
;
size_t
i
;
/* Junction targets must be the unparsed name, starting with \??\, using
* backslashes instead of forward, and end in a trailing backslash.
* eg: \??\C:\Foo\
*/
git_buf_puts
(
&
unparsed_buf
,
"
\\
??
\\
"
);
for
(
i
=
0
;
i
<
strlen
(
old
);
i
++
)
git_buf_putc
(
&
unparsed_buf
,
old
[
i
]
==
'/'
?
'\\'
:
old
[
i
]);
git_buf_putc
(
&
unparsed_buf
,
'\\'
);
subst_utf16_len
=
git__utf8_to_16
(
NULL
,
0
,
git_buf_cstr
(
&
unparsed_buf
));
subst_byte_len
=
subst_utf16_len
*
sizeof
(
WCHAR
);
print_utf16_len
=
subst_utf16_len
-
4
;
print_byte_len
=
subst_byte_len
-
(
4
*
sizeof
(
WCHAR
));
/* The junction must be an empty directory before the junction attribute
* can be added.
*/
cl_win32_pass
(
CreateDirectoryA
(
new
,
NULL
));
handle
=
CreateFileA
(
new
,
GENERIC_WRITE
,
0
,
NULL
,
OPEN_EXISTING
,
FILE_FLAG_OPEN_REPARSE_POINT
|
FILE_FLAG_BACKUP_SEMANTICS
,
NULL
);
cl_win32_pass
(
handle
!=
INVALID_HANDLE_VALUE
);
reparse_buflen
=
(
USHORT
)(
REPARSE_DATA_HEADER_SIZE
+
REPARSE_DATA_MOUNTPOINT_HEADER_SIZE
+
subst_byte_len
+
sizeof
(
WCHAR
)
+
print_byte_len
+
sizeof
(
WCHAR
));
reparse_buf
=
LocalAlloc
(
LMEM_FIXED
|
LMEM_ZEROINIT
,
reparse_buflen
);
cl_assert
(
reparse_buf
);
subst_utf16
=
reparse_buf
->
MountPointReparseBuffer
.
PathBuffer
;
print_utf16
=
subst_utf16
+
subst_utf16_len
+
1
;
ret
=
git__utf8_to_16
(
subst_utf16
,
subst_utf16_len
+
1
,
git_buf_cstr
(
&
unparsed_buf
));
cl_assert_equal_i
(
subst_utf16_len
,
ret
);
ret
=
git__utf8_to_16
(
print_utf16
,
print_utf16_len
+
1
,
git_buf_cstr
(
&
unparsed_buf
)
+
4
);
cl_assert_equal_i
(
print_utf16_len
,
ret
);
reparse_buf
->
ReparseTag
=
IO_REPARSE_TAG_MOUNT_POINT
;
reparse_buf
->
MountPointReparseBuffer
.
SubstituteNameOffset
=
0
;
reparse_buf
->
MountPointReparseBuffer
.
SubstituteNameLength
=
subst_byte_len
;
reparse_buf
->
MountPointReparseBuffer
.
PrintNameOffset
=
(
USHORT
)(
subst_byte_len
+
sizeof
(
WCHAR
));
reparse_buf
->
MountPointReparseBuffer
.
PrintNameLength
=
print_byte_len
;
reparse_buf
->
ReparseDataLength
=
reparse_buflen
-
REPARSE_DATA_HEADER_SIZE
;
cl_win32_pass
(
DeviceIoControl
(
handle
,
FSCTL_SET_REPARSE_POINT
,
reparse_buf
,
reparse_buflen
,
NULL
,
0
,
&
ioctl_ret
,
NULL
));
CloseHandle
(
handle
);
LocalFree
(
reparse_buf
);
}
static
void
do_custom_reparse
(
const
char
*
path
)
{
REPARSE_GUID_DATA_BUFFER
*
reparse_buf
;
HANDLE
handle
;
DWORD
ioctl_ret
;
const
char
*
reparse_data
=
"Reparse points are silly."
;
size_t
reparse_buflen
=
REPARSE_GUID_DATA_BUFFER_HEADER_SIZE
+
strlen
(
reparse_data
)
+
1
;
reparse_buf
=
LocalAlloc
(
LMEM_FIXED
|
LMEM_ZEROINIT
,
reparse_buflen
);
cl_assert
(
reparse_buf
);
reparse_buf
->
ReparseTag
=
42
;
reparse_buf
->
ReparseDataLength
=
(
WORD
)(
strlen
(
reparse_data
)
+
1
);
reparse_buf
->
ReparseGuid
.
Data1
=
0xdeadbeef
;
reparse_buf
->
ReparseGuid
.
Data2
=
0xdead
;
reparse_buf
->
ReparseGuid
.
Data3
=
0xbeef
;
reparse_buf
->
ReparseGuid
.
Data4
[
0
]
=
42
;
reparse_buf
->
ReparseGuid
.
Data4
[
1
]
=
42
;
reparse_buf
->
ReparseGuid
.
Data4
[
2
]
=
42
;
reparse_buf
->
ReparseGuid
.
Data4
[
3
]
=
42
;
reparse_buf
->
ReparseGuid
.
Data4
[
4
]
=
42
;
reparse_buf
->
ReparseGuid
.
Data4
[
5
]
=
42
;
reparse_buf
->
ReparseGuid
.
Data4
[
6
]
=
42
;
reparse_buf
->
ReparseGuid
.
Data4
[
7
]
=
42
;
reparse_buf
->
ReparseGuid
.
Data4
[
8
]
=
42
;
memcpy
(
reparse_buf
->
GenericReparseBuffer
.
DataBuffer
,
reparse_data
,
strlen
(
reparse_data
)
+
1
);
handle
=
CreateFileA
(
path
,
GENERIC_WRITE
,
0
,
NULL
,
OPEN_EXISTING
,
FILE_FLAG_OPEN_REPARSE_POINT
|
FILE_FLAG_BACKUP_SEMANTICS
,
NULL
);
cl_win32_pass
(
handle
!=
INVALID_HANDLE_VALUE
);
cl_win32_pass
(
DeviceIoControl
(
handle
,
FSCTL_SET_REPARSE_POINT
,
reparse_buf
,
reparse_buf
->
ReparseDataLength
+
REPARSE_GUID_DATA_BUFFER_HEADER_SIZE
,
NULL
,
0
,
&
ioctl_ret
,
NULL
));
CloseHandle
(
handle
);
LocalFree
(
reparse_buf
);
}
#endif
git_buf
*
unslashify
(
git_buf
*
buf
)
{
#ifdef GIT_WIN32
size_t
i
;
for
(
i
=
0
;
i
<
buf
->
size
;
i
++
)
if
(
buf
->
ptr
[
i
]
==
'/'
)
buf
->
ptr
[
i
]
=
'\\'
;
#endif
return
buf
;
}
void
test_core_link__stat_regular_file
(
void
)
{
struct
stat
st
;
cl_git_rewritefile
(
"stat_regfile"
,
"This is a regular file!
\n
"
);
cl_must_pass
(
p_stat
(
"stat_regfile"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
24
,
st
.
st_size
);
}
void
test_core_link__lstat_regular_file
(
void
)
{
struct
stat
st
;
cl_git_rewritefile
(
"lstat_regfile"
,
"This is a regular file!
\n
"
);
cl_must_pass
(
p_stat
(
"lstat_regfile"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
24
,
st
.
st_size
);
}
void
test_core_link__stat_symlink
(
void
)
{
struct
stat
st
;
cl_git_rewritefile
(
"stat_target"
,
"This is the target of a symbolic link.
\n
"
);
do_symlink
(
"stat_target"
,
"stat_symlink"
,
0
);
cl_must_pass
(
p_stat
(
"stat_target"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
39
,
st
.
st_size
);
cl_must_pass
(
p_stat
(
"stat_symlink"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
39
,
st
.
st_size
);
}
void
test_core_link__stat_symlink_directory
(
void
)
{
struct
stat
st
;
p_mkdir
(
"stat_dirtarget"
,
0777
);
do_symlink
(
"stat_dirtarget"
,
"stat_dirlink"
,
1
);
cl_must_pass
(
p_stat
(
"stat_dirtarget"
,
&
st
));
cl_assert
(
S_ISDIR
(
st
.
st_mode
));
cl_must_pass
(
p_stat
(
"stat_dirlink"
,
&
st
));
cl_assert
(
S_ISDIR
(
st
.
st_mode
));
}
void
test_core_link__stat_symlink_chain
(
void
)
{
struct
stat
st
;
cl_git_rewritefile
(
"stat_final_target"
,
"Final target of some symbolic links...
\n
"
);
do_symlink
(
"stat_final_target"
,
"stat_chain_3"
,
0
);
do_symlink
(
"stat_chain_3"
,
"stat_chain_2"
,
0
);
do_symlink
(
"stat_chain_2"
,
"stat_chain_1"
,
0
);
cl_must_pass
(
p_stat
(
"stat_chain_1"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
39
,
st
.
st_size
);
}
void
test_core_link__stat_dangling_symlink
(
void
)
{
struct
stat
st
;
do_symlink
(
"stat_nonexistent"
,
"stat_dangling"
,
0
);
cl_must_fail
(
p_stat
(
"stat_nonexistent"
,
&
st
));
cl_must_fail
(
p_stat
(
"stat_dangling"
,
&
st
));
}
void
test_core_link__stat_dangling_symlink_directory
(
void
)
{
struct
stat
st
;
do_symlink
(
"stat_nonexistent"
,
"stat_dangling_dir"
,
1
);
cl_must_fail
(
p_stat
(
"stat_nonexistent_dir"
,
&
st
));
cl_must_fail
(
p_stat
(
"stat_dangling"
,
&
st
));
}
void
test_core_link__lstat_symlink
(
void
)
{
git_buf
target_path
=
GIT_BUF_INIT
;
struct
stat
st
;
/* Windows always writes the canonical path as the link target, so
* write the full path on all platforms.
*/
git_buf_join
(
&
target_path
,
'/'
,
clar_sandbox_path
(),
"lstat_target"
);
cl_git_rewritefile
(
"lstat_target"
,
"This is the target of a symbolic link.
\n
"
);
do_symlink
(
git_buf_cstr
(
&
target_path
),
"lstat_symlink"
,
0
);
cl_must_pass
(
p_lstat
(
"lstat_target"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
39
,
st
.
st_size
);
cl_must_pass
(
p_lstat
(
"lstat_symlink"
,
&
st
));
cl_assert
(
S_ISLNK
(
st
.
st_mode
));
cl_assert_equal_i
(
git_buf_len
(
&
target_path
),
st
.
st_size
);
git_buf_free
(
&
target_path
);
}
void
test_core_link__lstat_symlink_directory
(
void
)
{
git_buf
target_path
=
GIT_BUF_INIT
;
struct
stat
st
;
git_buf_join
(
&
target_path
,
'/'
,
clar_sandbox_path
(),
"lstat_dirtarget"
);
p_mkdir
(
"lstat_dirtarget"
,
0777
);
do_symlink
(
git_buf_cstr
(
&
target_path
),
"lstat_dirlink"
,
1
);
cl_must_pass
(
p_lstat
(
"lstat_dirtarget"
,
&
st
));
cl_assert
(
S_ISDIR
(
st
.
st_mode
));
cl_must_pass
(
p_lstat
(
"lstat_dirlink"
,
&
st
));
cl_assert
(
S_ISLNK
(
st
.
st_mode
));
cl_assert_equal_i
(
git_buf_len
(
&
target_path
),
st
.
st_size
);
git_buf_free
(
&
target_path
);
}
void
test_core_link__lstat_dangling_symlink
(
void
)
{
struct
stat
st
;
do_symlink
(
"lstat_nonexistent"
,
"lstat_dangling"
,
0
);
cl_must_fail
(
p_lstat
(
"lstat_nonexistent"
,
&
st
));
cl_must_pass
(
p_lstat
(
"lstat_dangling"
,
&
st
));
cl_assert
(
S_ISLNK
(
st
.
st_mode
));
cl_assert_equal_i
(
strlen
(
"lstat_nonexistent"
),
st
.
st_size
);
}
void
test_core_link__lstat_dangling_symlink_directory
(
void
)
{
struct
stat
st
;
do_symlink
(
"lstat_nonexistent"
,
"lstat_dangling_dir"
,
1
);
cl_must_fail
(
p_lstat
(
"lstat_nonexistent"
,
&
st
));
cl_must_pass
(
p_lstat
(
"lstat_dangling_dir"
,
&
st
));
cl_assert
(
S_ISLNK
(
st
.
st_mode
));
cl_assert_equal_i
(
strlen
(
"lstat_nonexistent"
),
st
.
st_size
);
}
void
test_core_link__stat_junction
(
void
)
{
#ifdef GIT_WIN32
git_buf
target_path
=
GIT_BUF_INIT
;
struct
stat
st
;
git_buf_join
(
&
target_path
,
'/'
,
clar_sandbox_path
(),
"stat_junctarget"
);
p_mkdir
(
"stat_junctarget"
,
0777
);
do_junction
(
git_buf_cstr
(
&
target_path
),
"stat_junction"
);
cl_must_pass
(
p_stat
(
"stat_junctarget"
,
&
st
));
cl_assert
(
S_ISDIR
(
st
.
st_mode
));
cl_must_pass
(
p_stat
(
"stat_junction"
,
&
st
));
cl_assert
(
S_ISDIR
(
st
.
st_mode
));
git_buf_free
(
&
target_path
);
#endif
}
void
test_core_link__stat_dangling_junction
(
void
)
{
#ifdef GIT_WIN32
git_buf
target_path
=
GIT_BUF_INIT
;
struct
stat
st
;
git_buf_join
(
&
target_path
,
'/'
,
clar_sandbox_path
(),
"stat_nonexistent_junctarget"
);
p_mkdir
(
"stat_nonexistent_junctarget"
,
0777
);
do_junction
(
git_buf_cstr
(
&
target_path
),
"stat_dangling_junction"
);
RemoveDirectory
(
"stat_nonexistent_junctarget"
);
cl_must_fail
(
p_stat
(
"stat_nonexistent_junctarget"
,
&
st
));
cl_must_fail
(
p_stat
(
"stat_dangling_junction"
,
&
st
));
git_buf_free
(
&
target_path
);
#endif
}
void
test_core_link__lstat_junction
(
void
)
{
#ifdef GIT_WIN32
git_buf
target_path
=
GIT_BUF_INIT
;
struct
stat
st
;
git_buf_join
(
&
target_path
,
'/'
,
clar_sandbox_path
(),
"lstat_junctarget"
);
p_mkdir
(
"lstat_junctarget"
,
0777
);
do_junction
(
git_buf_cstr
(
&
target_path
),
"lstat_junction"
);
cl_must_pass
(
p_lstat
(
"lstat_junctarget"
,
&
st
));
cl_assert
(
S_ISDIR
(
st
.
st_mode
));
cl_must_pass
(
p_lstat
(
"lstat_junction"
,
&
st
));
cl_assert
(
S_ISLNK
(
st
.
st_mode
));
git_buf_free
(
&
target_path
);
#endif
}
void
test_core_link__lstat_dangling_junction
(
void
)
{
#ifdef GIT_WIN32
git_buf
target_path
=
GIT_BUF_INIT
;
struct
stat
st
;
git_buf_join
(
&
target_path
,
'/'
,
clar_sandbox_path
(),
"lstat_nonexistent_junctarget"
);
p_mkdir
(
"lstat_nonexistent_junctarget"
,
0777
);
do_junction
(
git_buf_cstr
(
&
target_path
),
"lstat_dangling_junction"
);
RemoveDirectory
(
"lstat_nonexistent_junctarget"
);
cl_must_fail
(
p_lstat
(
"lstat_nonexistent_junctarget"
,
&
st
));
cl_must_pass
(
p_lstat
(
"lstat_dangling_junction"
,
&
st
));
cl_assert
(
S_ISLNK
(
st
.
st_mode
));
cl_assert_equal_i
(
git_buf_len
(
&
target_path
),
st
.
st_size
);
git_buf_free
(
&
target_path
);
#endif
}
void
test_core_link__stat_hardlink
(
void
)
{
struct
stat
st
;
cl_git_rewritefile
(
"stat_hardlink1"
,
"This file has many names!
\n
"
);
do_hardlink
(
"stat_hardlink1"
,
"stat_hardlink2"
);
cl_must_pass
(
p_stat
(
"stat_hardlink1"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
26
,
st
.
st_size
);
cl_must_pass
(
p_stat
(
"stat_hardlink2"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
26
,
st
.
st_size
);
}
void
test_core_link__lstat_hardlink
(
void
)
{
struct
stat
st
;
cl_git_rewritefile
(
"lstat_hardlink1"
,
"This file has many names!
\n
"
);
do_hardlink
(
"lstat_hardlink1"
,
"lstat_hardlink2"
);
cl_must_pass
(
p_lstat
(
"lstat_hardlink1"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
26
,
st
.
st_size
);
cl_must_pass
(
p_lstat
(
"lstat_hardlink2"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
26
,
st
.
st_size
);
}
void
test_core_link__stat_reparse_point
(
void
)
{
#ifdef GIT_WIN32
struct
stat
st
;
/* Generic reparse points should be treated as regular files, only
* symlinks and junctions should be treated as links.
*/
cl_git_rewritefile
(
"stat_reparse"
,
"This is a reparse point!
\n
"
);
do_custom_reparse
(
"stat_reparse"
);
cl_must_pass
(
p_lstat
(
"stat_reparse"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
25
,
st
.
st_size
);
#endif
}
void
test_core_link__lstat_reparse_point
(
void
)
{
#ifdef GIT_WIN32
struct
stat
st
;
cl_git_rewritefile
(
"lstat_reparse"
,
"This is a reparse point!
\n
"
);
do_custom_reparse
(
"lstat_reparse"
);
cl_must_pass
(
p_lstat
(
"lstat_reparse"
,
&
st
));
cl_assert
(
S_ISREG
(
st
.
st_mode
));
cl_assert_equal_i
(
25
,
st
.
st_size
);
#endif
}
void
test_core_link__readlink_nonexistent_file
(
void
)
{
char
buf
[
2048
];
cl_must_fail
(
p_readlink
(
"readlink_nonexistent"
,
buf
,
2048
));
cl_assert_equal_i
(
ENOENT
,
errno
);
}
void
test_core_link__readlink_normal_file
(
void
)
{
char
buf
[
2048
];
cl_git_rewritefile
(
"readlink_regfile"
,
"This is a regular file!
\n
"
);
cl_must_fail
(
p_readlink
(
"readlink_regfile"
,
buf
,
2048
));
cl_assert_equal_i
(
EINVAL
,
errno
);
}
void
test_core_link__readlink_symlink
(
void
)
{
git_buf
target_path
=
GIT_BUF_INIT
;
int
len
;
char
buf
[
2048
];
git_buf_join
(
&
target_path
,
'/'
,
clar_sandbox_path
(),
"readlink_target"
);
cl_git_rewritefile
(
"readlink_target"
,
"This is the target of a symlink
\n
"
);
do_symlink
(
git_buf_cstr
(
&
target_path
),
"readlink_link"
,
0
);
len
=
p_readlink
(
"readlink_link"
,
buf
,
2048
);
cl_must_pass
(
len
);
buf
[
len
]
=
0
;
cl_assert_equal_s
(
git_buf_cstr
(
unslashify
(
&
target_path
)),
buf
);
git_buf_free
(
&
target_path
);
}
void
test_core_link__readlink_dangling
(
void
)
{
git_buf
target_path
=
GIT_BUF_INIT
;
int
len
;
char
buf
[
2048
];
git_buf_join
(
&
target_path
,
'/'
,
clar_sandbox_path
(),
"readlink_nonexistent"
);
do_symlink
(
git_buf_cstr
(
&
target_path
),
"readlink_dangling"
,
0
);
len
=
p_readlink
(
"readlink_dangling"
,
buf
,
2048
);
cl_must_pass
(
len
);
buf
[
len
]
=
0
;
cl_assert_equal_s
(
git_buf_cstr
(
unslashify
(
&
target_path
)),
buf
);
git_buf_free
(
&
target_path
);
}
void
test_core_link__readlink_multiple
(
void
)
{
git_buf
target_path
=
GIT_BUF_INIT
,
path3
=
GIT_BUF_INIT
,
path2
=
GIT_BUF_INIT
,
path1
=
GIT_BUF_INIT
;
int
len
;
char
buf
[
2048
];
git_buf_join
(
&
target_path
,
'/'
,
clar_sandbox_path
(),
"readlink_final"
);
git_buf_join
(
&
path3
,
'/'
,
clar_sandbox_path
(),
"readlink_3"
);
git_buf_join
(
&
path2
,
'/'
,
clar_sandbox_path
(),
"readlink_2"
);
git_buf_join
(
&
path1
,
'/'
,
clar_sandbox_path
(),
"readlink_1"
);
do_symlink
(
git_buf_cstr
(
&
target_path
),
git_buf_cstr
(
&
path3
),
0
);
do_symlink
(
git_buf_cstr
(
&
path3
),
git_buf_cstr
(
&
path2
),
0
);
do_symlink
(
git_buf_cstr
(
&
path2
),
git_buf_cstr
(
&
path1
),
0
);
len
=
p_readlink
(
"readlink_1"
,
buf
,
2048
);
cl_must_pass
(
len
);
buf
[
len
]
=
0
;
cl_assert_equal_s
(
git_buf_cstr
(
unslashify
(
&
path2
)),
buf
);
git_buf_free
(
&
path1
);
git_buf_free
(
&
path2
);
git_buf_free
(
&
path3
);
git_buf_free
(
&
target_path
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment