Add 'src/libpsl-native/' from commit '4bb241ad11c54985617343fbeb8700cfdcdf7ae0'

git-subtree-dir: src/libpsl-native
git-subtree-mainline: 2264613ae3
git-subtree-split: 4bb241ad11
This commit is contained in:
Andrew Schwartzmeyer
2016-04-01 17:59:08 -07:00
44 changed files with 1795 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
CMakeCache.txt
CMakeFiles
CMakeScripts
Makefile
cmake_install.cmake
install_manifest.txt
CTestTestfile.cmake
Testing/
test/psl-native-test
src/libpsl-native.so
src/libpsl-native.dylib
test/native-tests.xml
+4
View File
@@ -0,0 +1,4 @@
[submodule "test/googletest"]
path = test/googletest
url = https://github.com/google/googletest.git
ignore = dirty
+10
View File
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 2.8.4)
project(PSL-NATIVE)
add_compile_options(-std=c++11 -Wall -Werror)
set(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/../Microsoft.PowerShell.Linux.Host")
# test in BUILD_DIR
enable_testing()
add_subdirectory(src)
add_subdirectory(test)
+56
View File
@@ -0,0 +1,56 @@
# libpsl-native
This library provides functionality missing from .NET Core via system calls,
that are called from from the `CorePsPlatform.cs` file of PowerShell. The
method to do this is a Platform Invoke, which is C#'s Foreign Function
Interface to C code (and C++ by way of `extern C`).
## Build
[CMake][] is used to build the project, which results in a `libpsl-native.so`
library on Linux, and `libpsl-native.dylib` on OS X.
```sh
cmake -DCMAKE_BUILD_TYPE=Debug .
make -j
```
[CMake]: https://cmake.org/cmake/help/v2.8.12/cmake.html
## Test
The [Google Test][] framework is used for unit tests.
Use either `make test` or `ctest --verbose` for more output.
[Google Test]: https://github.com/google/googletest/tree/release-1.7.0
## Notes
Marshalling data from native to managed code is much easier on Linux than it is
on Windows. For instance, to return a string, you simply return a copy of it on
the heap. Since only one memory allocator is used on Linux, the .NET runtime
has no problem later freeing the buffer. Additionally, .NET presumes that the
codepage "Ansi" on Linux is always UTF-8. So just marshal the string as
`UnmanagedType.LPStr`.
### C# (Managed)
```c#
[DllImport("libpsl-native", CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static extern string GetSomeString();
```
### C (Native)
```c
char *GetSomeString()
{
return strdup("some string");
}
```
The CoreFX team has an excellent guide for [UNIX Interop][].
[UNIX Interop]: https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/interop-guidelines.md#unix-shims
+14
View File
@@ -0,0 +1,14 @@
add_library(psl-native SHARED
getcurrentprocessorid.cpp
getusername.cpp
getcomputername.cpp
getlinkcount.cpp
getfullyqualifiedname.cpp
issymlink.cpp
isexecutable.cpp
setdate.cpp
createhardlink.cpp
createsymlink.cpp
followsymlink.cpp)
target_include_directories(psl-native PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+115
View File
@@ -0,0 +1,115 @@
//! @file createsymlink.cpp
//! @author George FLeming <v-geflem@microsoft.com>
//! @brief create new hard link
#include <errno.h>
#include <unistd.h>
#include <string>
#include "createhardlink.h"
//! @brief Createhardlink create new symbolic link
//!
//! Createhardlink
//!
//! @param[in] link
//! @parblock
//! A pointer to the buffer that contains the symbolic link to create
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @param[in] target
//! @parblock
//! A pointer to the buffer that contains the existing file
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//! - ERROR_FILE_NOT_FOUND: file does not exist
//! - ERROR_ACCESS_DENIED: access is denied
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
//! - ERROR_TOO_MANY_LINK: max number of hard links has been exceeded
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
//! - ERROR_BUFFER_OVERFLOW: file name is too long
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
//!
//! @retval 1 if creation is successful
//! @retval 0 if createion failed
//!
int32_t CreateHardLink(const char *newlink, const char *target)
{
errno = 0;
// Check parameters
if (!newlink || !target)
{
errno = ERROR_INVALID_PARAMETER;
return 0;
}
int returnCode = link(target, newlink);
if (returnCode == 0)
{
return 1;
}
switch(errno)
{
case EACCES:
errno = ERROR_ACCESS_DENIED;
break;
case EDQUOT:
errno = ERROR_DISK_FULL;
break;
case EEXIST:
errno = ERROR_FILE_EXISTS;
break;
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case EIO:
errno = ERROR_GEN_FAILURE;
break;
case ELOOP:
errno = ERROR_TOO_MANY_LINKS;
break;
case EMLINK:
errno = ERROR_TOO_MANY_LINKS;
break;
case ENAMETOOLONG:
errno = ERROR_BAD_PATH_NAME;
break;
case ENOENT:
errno = ERROR_FILE_NOT_FOUND;
break;
case ENOMEM:
errno = ERROR_OUTOFMEMORY;
break;
case ENOTDIR:
errno = ERROR_INVALID_NAME;
break;
case ENOSPC:
errno = ERROR_DISK_FULL;
break;
case EPERM:
errno = ERROR_ACCESS_DENIED;
break;
case EROFS:
errno = ERROR_ACCESS_DENIED;
break;
case EXDEV:
errno = ERROR_GEN_FAILURE;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
int32_t CreateHardLink(const char *link, const char *target);
PAL_END_EXTERNC
+106
View File
@@ -0,0 +1,106 @@
//! @file createsymlink.cpp
//! @author George FLeming <v-geflem@microsoft.com>
//! @brief create new symbolic link
#include <errno.h>
#include <unistd.h>
#include <string>
#include "createsymlink.h"
//! @brief Createsymlink create new symbolic link
//!
//! Createsymlink
//!
//! @param[in] link
//! @parblock
//! A pointer to the buffer that contains the symbolic link to create
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @param[in] target
//! @parblock
//! A pointer to the buffer that contains the existing file
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//! - ERROR_FILE_NOT_FOUND: file does not exist
//! - ERROR_ACCESS_DENIED: access is denied
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
//! - ERROR_STOPPED_ON_SYMLINK: the operation stopped after reaching a symbolic link
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
//! - ERROR_BUFFER_OVERFLOW: file name is too long
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
//!
//! @retval 1 if creation is successful
//! @retval 0 if createion failed
//!
int32_t CreateSymLink(const char *link, const char *target)
{
errno = 0;
// Check parameters
if (!link || !target)
{
errno = ERROR_INVALID_PARAMETER;
return 0;
}
int returnCode = symlink(target, link);
if (returnCode == 0)
{
return 1;
}
switch(errno)
{
case EACCES:
errno = ERROR_ACCESS_DENIED;
break;
case EDQUOT:
errno = ERROR_DISK_FULL;
break;
case EEXIST:
errno = ERROR_FILE_EXISTS;
break;
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case EIO:
errno = ERROR_GEN_FAILURE;
break;
case ELOOP:
errno = ERROR_STOPPED_ON_SYMLINK;
break;
case ENAMETOOLONG:
errno = ERROR_BAD_PATH_NAME;
break;
case ENOENT:
errno = ERROR_FILE_NOT_FOUND;
break;
case ENOMEM:
errno = ERROR_OUTOFMEMORY;
break;
case ENOTDIR:
errno = ERROR_INVALID_NAME;
break;
case ENOSPC:
errno = ERROR_DISK_FULL;
break;
case EPERM:
errno = ERROR_GEN_FAILURE;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
int32_t CreateSymLink(const char *link, const char *target);
PAL_END_EXTERNC
+90
View File
@@ -0,0 +1,90 @@
//! @file followSymLink.cpp
//! @author George FLeming <v-geflem@microsoft.com>
//! @brief returns whether a path is a symbolic link
#include <errno.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include "followsymlink.h"
//! @brief Followsymlink determines target path of a sym link
//!
//! Followsymlink
//!
//! @param[in] fileName
//! @parblock
//! A pointer to the buffer that contains the file name
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//! - ERROR_FILE_NOT_FOUND: file does not exist
//! - ERROR_ACCESS_DENIED: access is denied
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
//! - ERROR_STOPPED_ON_SYMLINK: too many symbolic links
//! - ERROR_GEN_FAILURE: I/O error occurred
//! - ERROR_INVALID_NAME: file provided is not a symbolic link
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_BAD_PATH_NAME: pathname is too long
//! - ERROR_OUTOFMEMORY insufficient kernal memory
//!
//! @retval target path, or NULL if unsuccessful
//!
char* FollowSymLink(const char* fileName)
{
errno = 0;
// Check parameters
if (!fileName)
{
errno = ERROR_INVALID_PARAMETER;
return NULL;
}
char buffer[PATH_MAX];
ssize_t sz = readlink(fileName, buffer, PATH_MAX);
if (sz == -1)
{
switch(errno)
{
case EACCES:
errno = ERROR_ACCESS_DENIED;
break;
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case EINVAL:
errno = ERROR_INVALID_NAME;
break;
case EIO:
errno = ERROR_GEN_FAILURE;
break;
case ELOOP:
errno = ERROR_STOPPED_ON_SYMLINK;
break;
case ENAMETOOLONG:
errno = ERROR_BAD_PATH_NAME;
break;
case ENOENT:
errno = ERROR_FILE_NOT_FOUND;
break;
case ENOMEM:
errno = ERROR_OUTOFMEMORY;
break;
case ENOTDIR:
errno = ERROR_BAD_PATH_NAME;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return NULL;
}
buffer[sz] = '\0';
return strndup(buffer, sz + 1);
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
char* FollowSymLink(const char* fileName);
PAL_END_EXTERNC
+45
View File
@@ -0,0 +1,45 @@
//! @file getcomputername.cpp
//! @author George Fleming <v-geflem@microsoft>
//! @brief Implements GetComputerName Win32 API
#include <errno.h>
#include <unistd.h>
#include <string>
#include "getcomputername.h"
//! @brief GetComputerName retrieves the name of the host associated with
//! the current thread.
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_FUNCTION: getlogin_r() returned an unrecognized error code
//! - ERROR_INVALID_ADDRESS: buffer is an invalid address
//! - ERROR_GEN_FAILURE: buffer not large enough
//!
//! @retval username as UTF-8 string, or null if unsuccessful
char* GetComputerName()
{
errno = 0;
// Get computername from system, note that gethostname(2) gets the
// nodename from uname
std::string computername(_POSIX_HOST_NAME_MAX, 0);
int err = gethostname(&computername[0], computername.length());
// Map errno to Win32 Error Codes
if (err != 0)
{
switch (errno)
{
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case ENAMETOOLONG:
errno = ERROR_GEN_FAILURE;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return NULL;
}
return strdup(computername.c_str());
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
char *GetComputerName();
PAL_END_EXTERNC
@@ -0,0 +1,9 @@
#include "getcurrentprocessorid.h"
#include <unistd.h>
int32_t GetCurrentProcessId()
{
pid_t pid = getpid();
return static_cast<int32_t>(pid);
}
@@ -0,0 +1,10 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
int32_t GetCurrentProcessId();
PAL_END_EXTERNC
@@ -0,0 +1,9 @@
#include "getcurrentthreadid.h"
#include <unistd.h>
#include <pthread.h>
HANDLE GetCurrentThreadId()
{
pid_t tid = pthread_self();
return reinterpret_cast<HANDLE>(tid);
}
@@ -0,0 +1,10 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
HANDLE GetCurrentThreadId();
PAL_END_EXTERNC
@@ -0,0 +1,64 @@
//! @file getfullyqualifiedname.cpp
//! @author George Fleming <v-geflem@microsoft.com>
//! @brief Implements GetFullyQualifiedName on Linux
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "getcomputername.h"
#include "getfullyqualifiedname.h"
//! @brief GetFullyQualifiedName retrieves the fully qualifed dns name of the host
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_FUNCTION: getlogin_r() returned an unrecognized error code (from GetComputerName)
//! - ERROR_INVALID_ADDRESS: buffer is an invalid address (from GetComputerName)
//! - ERROR_GEN_FAILURE: buffer not large enough (from GetComputerName)
//! - ERROR_BAD_NET_NAME: Cannot determine network short name
//!
//! @retval username as UTF-8 string, or null if unsuccessful
//!
char *GetFullyQualifiedName()
{
errno = 0;
char *computerName = GetComputerName();
if (computerName == NULL)
{
return NULL;
}
if (strchr(computerName, '.') != NULL)
{
return computerName;
}
struct addrinfo hints, *info;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; /*either IPV4 or IPV6*/
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
/* There are several ways to get the domain name:
* uname(2), gethostbyname(3), resolver(3), getdomainname(2),
* and getaddrinfo(3). Some of these are not portable, some aren't
* POSIX compliant, and some are being deprecated. getaddrinfo seems
* to be the best choice.
*/
if (getaddrinfo(computerName, "http", &hints, &info) != 0)
{
errno = ERROR_BAD_NET_NAME;
return NULL;
}
// info is actually a link-list. We'll just return the first full name
char *fullName = strndup(info->ai_canonname, strlen(info->ai_canonname));
freeaddrinfo(info);
free(computerName);
return fullName;
}
@@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
char *GetFullyQualifiedName();
PAL_END_EXTERNC
+104
View File
@@ -0,0 +1,104 @@
//! @file getlinkcount.cpp
//! @author George FLeming <v-geflem@microsoft.com>
//! @brief Retrieve link count of a file
#include <errno.h>
#include <locale.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include "getlinkcount.h"
//! @brief GetLinkCount retrieves the file link count (number of hard links)
//! for the given file
//!
//! GetLinkCount
//!
//! @param[in] fileName
//! @parblock
//! A pointer to the buffer that contains the file name
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @param[out] count
//! @parblock
//! This function returns the number of hard links associated with this file
//! @endparblock
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//! - ERROR_FILE_NOT_FOUND: file does not exist
//! - ERROR_ACCESS_DENIED: access is denied
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
//! - ERROR_STOPPED_ON_SYMLINK: the operation stopped after reaching a symbolic link
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
//! - ERROR_BUFFER_OVERFLOW: file name is too long
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
//!
//! @retval 1 If the function succeeds, and the variable pointed to by buffer contains
//! infomation about the files
//! @retval 0 If the function fails, the return value is zero. To get
//! extended error information, call GetLastError.
//!
int32_t GetLinkCount(const char* fileName, int32_t *count)
{
errno = 0;
// Check parameters
if (!fileName)
{
errno = ERROR_INVALID_PARAMETER;
return 0;
}
struct stat statBuf;
int returnCode = lstat(fileName, &statBuf);
if (returnCode != 0)
{
switch(errno)
{
case EACCES:
errno = ERROR_ACCESS_DENIED;
break;
case EBADF:
errno = ERROR_FILE_NOT_FOUND;
break;
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case ELOOP:
errno = ERROR_STOPPED_ON_SYMLINK;
break;
case ENAMETOOLONG:
errno = ERROR_GEN_FAILURE;
break;
case ENOENT:
errno = ERROR_FILE_NOT_FOUND;
break;
case ENOMEM:
errno = ERROR_NO_SUCH_USER;
break;
case ENOTDIR:
errno = ERROR_INVALID_NAME;
break;
case EOVERFLOW:
errno = ERROR_BUFFER_OVERFLOW;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return 0;
}
*count = statBuf.st_nlink;
return 1;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
int32_t GetLinkCount(const char* fileName, int32_t *count);
PAL_END_EXTERNC
+65
View File
@@ -0,0 +1,65 @@
//! @file getusername.cpp
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
//! @brief Implements GetUserName for Linux
#include <errno.h>
#include <locale.h>
#include <unistd.h>
#include <string>
#include <pwd.h>
#include "getusername.h"
//! @brief GetUserName retrieves the name of the user associated with
//! the current thread.
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//! - ERROR_NO_SUCH_USER: there was no corresponding user
//! - ERROR_GEN_FAILURE: sysconf() or getpwuid() failed for unknown reasons
//!
//! @retval username as UTF-8 string, or null if unsuccessful
char* GetUserName()
{
errno = 0;
struct passwd pwd;
struct passwd* result;
// gets the initial suggested size for buf
int buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
if (buflen == -1)
{
errno = ERROR_GEN_FAILURE;
return NULL;
}
std::string buf(buflen, 0);
// geteuid() gets the effective user ID of the calling process, and is always successful
int ret = getpwuid_r(geteuid(), &pwd, &buf[0], buflen, &result);
// Map errno to Win32 Error Codes
if (ret)
{
switch (errno)
{
case ENOENT:
case ESRCH:
case EBADF:
case EPERM:
errno = ERROR_NO_SUCH_USER;
break;
default:
errno = ERROR_GEN_FAILURE;
}
return NULL;
}
// Check if no user matched
if (result == NULL)
{
errno = ERROR_NO_SUCH_USER;
return NULL;
}
// allocate copy on heap so CLR can free it
return strdup(result->pw_name);
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
char* GetUserName();
PAL_END_EXTERNC
+81
View File
@@ -0,0 +1,81 @@
//! @file isExecutable.cpp
//! @author George FLeming <v-geflem@microsoft.com>
//! @brief returns whether a file is executable
#include <errno.h>
#include <unistd.h>
#include <string>
#include "isexecutable.h"
//! @brief IsExecutable determines if path is executable
//!
//! IsExecutable
//!
//! @param[in] fileName
//! @parblock
//! A pointer to the buffer that contains the file name
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_INVALID_PARAMETER: parameter to access(2) call is incorrect
//!
//! @retval 1 if path is an executable
//! @retval 0 if path is not a executable
//! @retval -1 If the function fails.. To get extended error information, call GetLastError.
//!
int32_t IsExecutable(const char* fileName)
{
errno = 0;
// Check parameters
if (!fileName)
{
errno = ERROR_INVALID_PARAMETER;
return -1;
}
int returnCode = access(fileName, X_OK);
if (returnCode == 0)
{
return 1;
}
switch(errno)
{
case EACCES:
return 0;
case EBADF:
case ENOENT:
errno = ERROR_FILE_NOT_FOUND;
break;
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case ELOOP:
errno = ERROR_STOPPED_ON_SYMLINK;
break;
case EIO:
case ENOMEM:
errno = ERROR_GEN_FAILURE;
break;
case ENOTDIR:
case ENAMETOOLONG:
errno = ERROR_INVALID_NAME;
break;
case EINVAL:
errno = ERROR_INVALID_PARAMETER;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return -1;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
int32_t IsExecutable(const char* fileName);
PAL_END_EXTERNC
+96
View File
@@ -0,0 +1,96 @@
//! @file isSymLink.cpp
//! @author George FLeming <v-geflem@microsoft.com>
//! @brief returns whether a path is a symbolic link
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include "issymlink.h"
//! @brief IsSymLink determines if path is a symbolic link
//!
//! IsSymLink
//!
//! @param[in] fileName
//! @parblock
//! A pointer to the buffer that contains the file name
//!
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
//! @endparblock
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//! - ERROR_FILE_NOT_FOUND: file does not exist
//! - ERROR_ACCESS_DENIED: access is denied
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
//! - ERROR_STOPPED_ON_SYMLINK: the operation stopped after reaching a symbolic link
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
//! - ERROR_BUFFER_OVERFLOW: file name is too long
//! - ERROR_INVALID_FUNCTION: incorrect function
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
//!
//! @retval 1 if path is a symbolic link
//! @retval 0 if path is not a symbolic link
//! @retval -1 If the function fails.. To get extended error information, call GetLastError.
//!
int32_t IsSymLink(const char* fileName)
{
errno = 0;
// Check parameters
if (!fileName)
{
errno = ERROR_INVALID_PARAMETER;
return -1;
}
struct stat statBuf;
int returnCode = lstat(fileName, &statBuf);
if (returnCode != 0)
{
switch(errno)
{
case EACCES:
errno = ERROR_ACCESS_DENIED;
break;
case EBADF:
errno = ERROR_FILE_NOT_FOUND;
break;
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case ELOOP:
errno = ERROR_STOPPED_ON_SYMLINK;
break;
case ENAMETOOLONG:
errno = ERROR_GEN_FAILURE;
break;
case ENOENT:
errno = ERROR_FILE_NOT_FOUND;
break;
case ENOMEM:
errno = ERROR_NO_SUCH_USER;
break;
case ENOTDIR:
errno = ERROR_INVALID_NAME;
break;
case EOVERFLOW:
errno = ERROR_BUFFER_OVERFLOW;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return -1;
}
return S_ISLNK(statBuf.st_mode) ? 1 : 0;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
int32_t IsSymLink(const char* fileName);
PAL_END_EXTERNC
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#define ERROR_INVALID_PARAMETER 87
#define ERROR_OUTOFMEMORY 14
#define ERROR_BAD_ENVIRONMENT 0x0000000A
#define ERROR_TOO_MANY_OPEN_FILES 0x00000004
#define ERROR_INSUFFICIENT_BUFFER 0x0000007A
#define ERROR_NO_ASSOCIATION 0x00000483
#define ERROR_NO_SUCH_USER 0x00000525
#define ERROR_INVALID_FUNCTION 0x00000001
#define ERROR_INVALID_ADDRESS 0x000001e7
#define ERROR_GEN_FAILURE 0x0000001F
#define ERROR_ACCESS_DENIED 0x00000005
#define ERROR_INVALID_NAME 0x0000007B
#define ERROR_STOPPED_ON_SYMLINK 0x000002A9
#define ERROR_BUFFER_OVERFLOW 0x0000006F
#define ERROR_FILE_NOT_FOUND 0x00000002
#define ERROR_BAD_PATH_NAME 0x000000A1
#define ERROR_BAD_NET_NAME 0x00000043
#define ERROR_DISK_FULL 0x00000070
#define ERROR_FILE_EXISTS 0x00000050
#define ERROR_TOO_MANY_LINKS 0x00000476
/*
**==============================================================================
**
** PAL_BEGIN_EXTERNC
** PAL_END_EXTERNC
**
**==============================================================================
*/
#if defined(__cplusplus)
# define PAL_BEGIN_EXTERNC extern "C" {
# define PAL_END_EXTERNC }
#else
# define PAL_BEGIN_EXTERNC
# define PAL_END_EXTERNC
#endif
+73
View File
@@ -0,0 +1,73 @@
//! @file setdate.cpp
//! @author George FLeming <v-geflem@microsoft.com>
//! @brief set local/system date and time
#include <errno.h>
#include <langinfo.h>
#include <locale.h>
#include <string>
#include <time.h>
#include <sys/time.h>
#include "setdate.h"
//! @brief SetDate sets the date and time on local computer. You must
//! be super-user to set the time.
//!
//! SetDate
//!
//! @param[in] info
//! @parblock
//! A struct that contains program to execute and its parameters
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_BAD_ENVIRONMENT: locale is not UTF-8
//! - ERROR_INVALID_PARAMETER: time was not passed in correctly
//! - ERROR_ACCESS_DENIED: you must be super-user to set the date
//!
//! @retval 0 successfully set date
//! @retval -1 if failure occurred. To get extended error information, call GetLastError.
//!
int32_t SetDate(const SetDateInfo &info)
{
errno = 0;
// Select locale from environment
setlocale(LC_ALL, "");
// Check that locale is UTF-8
if (nl_langinfo(CODESET) != std::string("UTF-8"))
{
errno = ERROR_BAD_ENVIRONMENT;
return -1;
}
struct tm bdTime;
struct timeval tv;
bdTime.tm_year = info.Year - 1900;
bdTime.tm_mon = info.Month - 1; // This is zero-based
bdTime.tm_mday = info.Day;
bdTime.tm_hour = info.Hour;
bdTime.tm_min = info.Minute;
bdTime.tm_sec = info.Second;
bdTime.tm_isdst = info.DST;
time_t newTime = mktime(&bdTime);
if (newTime == -1)
{
errno = ERROR_INVALID_PARAMETER;
return -1;
}
tv.tv_sec = newTime;
tv.tv_usec = 0;
int result = settimeofday(&tv, NULL);
if (result == -1)
{
errno = ERROR_ACCESS_DENIED;
return -1;
}
return 0;
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
typedef struct setDateInfo
{
// the order of members does matter here
int32_t Year;
int32_t Month;
int32_t Day;
int32_t Hour;
int32_t Minute;
int32_t Second;
int32_t Millisecond;
int32_t DST;
} SetDateInfo;
int32_t SetDate(const SetDateInfo &info);
PAL_END_EXTERNC
+22
View File
@@ -0,0 +1,22 @@
add_subdirectory(googletest)
add_executable(psl-native-test
test-locale.cpp
test-getcurrentprocessid.cpp
test-getusername.cpp
test-getcomputername.cpp
test-getlinkcount.cpp
test-getfullyqualifiedname.cpp
test-issymlink.cpp
test-isexecutable.cpp
test-createsymlink.cpp
test-createhardlink.cpp
main.cpp)
# manually include gtest headers
target_include_directories(psl-native-test PRIVATE ${gtest_SOURCE_DIR}/include)
target_link_libraries(psl-native-test psl-native gtest)
add_test(NAME psl-native-test
COMMAND psl-native-test --gtest_output=xml:native-tests.xml)
Submodule src/libpsl-native/test/googletest added at c99458533a
+7
View File
@@ -0,0 +1,7 @@
#include <gtest/gtest.h>
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -0,0 +1,91 @@
//! @file test-createhardlink.cpp
//! @author George Fleming <v-geflem@microsoft.com>
//! @brief Implements test for CreateHardLink()
#include <gtest/gtest.h>
#include <errno.h>
#include <unistd.h>
#include "getlinkcount.h"
#include "createhardlink.h"
using namespace std;
class CreateHardLinkTest : public ::testing::Test
{
protected:
static const int bufSize = 64;
const string fileTemplate = "/tmp/symlinktest.fXXXXXX";
const string dirTemplate = "/tmp/symlinktest.dXXXXXX";
const string fileHardLink = "/tmp/symlinktest.flink";
const string dirHardLink = "/tmp/symlinktest.dlink";
char *file, *dir;
char fileTemplateBuf[bufSize], dirTemplateBuf[bufSize];
CreateHardLinkTest()
{
// since mkstemp and mkdtemp modifies the template string, let's give them writable buffers
strcpy(fileTemplateBuf, fileTemplate.c_str());
strcpy(dirTemplateBuf, dirTemplate.c_str());
// First create a temp file
int fd = mkstemp(fileTemplateBuf);
EXPECT_TRUE(fd != -1);
file = fileTemplateBuf;
// Create a temp directory
dir = mkdtemp(dirTemplateBuf);
EXPECT_TRUE(dir != NULL);
// Create hard link to file
int ret1 = CreateHardLink(fileHardLink.c_str(), file);
EXPECT_EQ(ret1, 1);
// Create hard link to directory - should fail
int ret2 = CreateHardLink(dirHardLink.c_str(), dir);
EXPECT_EQ(ret2, 0);
}
~CreateHardLinkTest()
{
int ret;
ret = unlink(fileHardLink.c_str());
EXPECT_EQ(0, ret);
ret = unlink(file);
EXPECT_EQ(0, ret);
ret = rmdir(dir);
EXPECT_EQ(0, ret);
}
};
TEST_F(CreateHardLinkTest, FilePathNameIsNull)
{
int retVal = CreateHardLink(NULL, NULL);
EXPECT_EQ(retVal, 0);
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
}
TEST_F(CreateHardLinkTest, FilePathNameDoesNotExist)
{
std::string invalidFile = "/tmp/symlinktest_invalidFile";
std::string invalidLink = "/tmp/symlinktest_invalidLink";
// make sure neither exists
unlink(invalidFile.c_str());
unlink(invalidLink.c_str());
int retVal = CreateHardLink(invalidLink.c_str(), invalidFile.c_str());
EXPECT_EQ(retVal, 0);
}
TEST_F(CreateHardLinkTest, VerifyLinkCount)
{
int count = 0;
int retVal = GetLinkCount(fileHardLink.c_str(), &count);
EXPECT_EQ(1, retVal);
EXPECT_EQ(2, count);
}
@@ -0,0 +1,117 @@
//! @file test-createsymlink.cpp
//! @author George Fleming <v-geflem@microsoft.com>
//! @brief Implements test for CreateSymLink() and FollowSymLink()
#include <gtest/gtest.h>
#include <errno.h>
#include <unistd.h>
#include "issymlink.h"
#include "createsymlink.h"
#include "followsymlink.h"
using namespace std;
class CreateSymLinkTest : public ::testing::Test
{
protected:
static const int bufSize = 64;
const string fileTemplate = "/tmp/symlinktest.fXXXXXX";
const string dirTemplate = "/tmp/symlinktest.dXXXXXX";
const string fileSymLink = "/tmp/symlinktest.flink";
const string dirSymLink = "/tmp/symlinktest.dlink";
char *file, *dir;
char fileTemplateBuf[bufSize], dirTemplateBuf[bufSize];
CreateSymLinkTest()
{
// since mkstemp and mkdtemp modifies the template string, let's give them writable buffers
strcpy(fileTemplateBuf, fileTemplate.c_str());
strcpy(dirTemplateBuf, dirTemplate.c_str());
// First create a temp file
int fd = mkstemp(fileTemplateBuf);
EXPECT_TRUE(fd != -1);
file = fileTemplateBuf;
// Create a temp directory
dir = mkdtemp(dirTemplateBuf);
EXPECT_TRUE(dir != NULL);
// Create symbolic link to file
int ret1 = CreateSymLink(fileSymLink.c_str(), file);
EXPECT_EQ(ret1, 1);
// Create symbolic link to directory
int ret2 = CreateSymLink(dirSymLink.c_str(), dir);
EXPECT_EQ(ret2, 1);
}
~CreateSymLinkTest()
{
int ret;
ret = unlink(fileSymLink.c_str());
EXPECT_EQ(0, ret);
ret = unlink(dirSymLink.c_str());
EXPECT_EQ(0, ret);
ret = unlink(file);
EXPECT_EQ(0, ret);
ret = rmdir(dir);
EXPECT_EQ(0, ret);
}
};
TEST_F(CreateSymLinkTest, FilePathNameIsNull)
{
int retVal = CreateSymLink(NULL, NULL);
EXPECT_EQ(retVal, 0);
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
}
TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist)
{
std::string invalidFile = "/tmp/symlinktest_invalidFile";
std::string invalidLink = "/tmp/symlinktest_invalidLink";
// make sure neither exists
unlink(invalidFile.c_str());
unlink(invalidLink.c_str());
// Linux allows creation of symbolic link that points to an invalid file
int retVal = CreateSymLink(invalidLink.c_str(), invalidFile.c_str());
EXPECT_EQ(retVal, 1);
std::string target = FollowSymLink(invalidLink.c_str());
EXPECT_EQ(target, invalidFile);
unlink(invalidLink.c_str());
}
TEST_F(CreateSymLinkTest, SymLinkToFile)
{
int retVal = IsSymLink(fileSymLink.c_str());
EXPECT_EQ(1, retVal);
std::string target = FollowSymLink(fileSymLink.c_str());
EXPECT_EQ(target, file);
}
TEST_F(CreateSymLinkTest, SymLinkToDirectory)
{
int retVal = IsSymLink(dirSymLink.c_str());
EXPECT_EQ(1, retVal);
std::string target = FollowSymLink(dirSymLink.c_str());
EXPECT_EQ(target, dir);
}
TEST_F(CreateSymLinkTest, SymLinkAgain)
{
int retVal = CreateSymLink(fileSymLink.c_str(), file);
EXPECT_EQ(0, retVal);
EXPECT_EQ(ERROR_FILE_EXISTS, errno);
}
@@ -0,0 +1,33 @@
//! @file test-getcomputername.cpp
//! @author George Fleming <v-geflem@microsoft.com>
//! @brief Unit tests for GetComputerName
#include <gtest/gtest.h>
#include "getcomputername.h"
//! Test fixture for GetComputerNameTest
class GetComputerNameTest : public ::testing::Test
{
};
TEST_F(GetComputerNameTest, Success)
{
char expectedComputerName[_POSIX_HOST_NAME_MAX];
// the gethostname system call gets the nodename from uname
FILE *fPtr = popen("uname -n", "r");
ASSERT_TRUE(fPtr != NULL);
char *linePtr = fgets(expectedComputerName, sizeof(expectedComputerName), fPtr);
ASSERT_TRUE(linePtr != NULL);
// There's a tendency to have \n at end of fgets string, so remove it before compare
size_t sz = strlen(expectedComputerName);
if (sz > 0 && expectedComputerName[sz - 1] == '\n')
{
expectedComputerName[sz - 1] = '\0';
}
pclose(fPtr);
ASSERT_STREQ(GetComputerName(), expectedComputerName);
}
@@ -0,0 +1,16 @@
#include <gtest/gtest.h>
#include "getcurrentprocessorid.h"
// This is a very simple test case to show how tests can be written
TEST(GetCurrentProcessId,simple)
{
const int32_t currentProcessId = GetCurrentProcessId();
const pid_t pid = getpid();
// first make sure that on this platform those types are of the same size
ASSERT_TRUE(sizeof(int32_t) >= sizeof(pid_t));
// now compare the actual values
ASSERT_EQ(currentProcessId,static_cast<int32_t>(pid));
}
@@ -0,0 +1,16 @@
#include <gtest/gtest.h>
#include "getcurrentthreadid.h"
#include <pthread.h>
TEST(GetCurrentThreadId,simple)
{
const HANDLE currentThreadId = GetCurrentThreadId();
const pid_t tid = pthread_self();
// first make sure that on this platform those types are of the same size
ASSERT_TRUE(sizeof(HANDLE) >= sizeof(pid_t));
// now compare the actual values
ASSERT_EQ(currentThreadId,reinterpret_cast<HANDLE>(tid));
}
@@ -0,0 +1,40 @@
//! @file test-getfullyqualifiedname.cpp
//! @author George Fleming <v-geflem@microsoft.com>
//! @brief Unit tests for GetFullyQualifiedName
#include <gtest/gtest.h>
#include "getfullyqualifiedname.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string>
//! Test fixture for GetComputerNameTest
class GetFullyQualifiedNameTest : public ::testing::Test
{
};
TEST_F(GetFullyQualifiedNameTest, ValidateLinuxGetFullyQualifiedDomainName)
{
std::string actual(GetFullyQualifiedName());
std::string hostname(_POSIX_HOST_NAME_MAX, 0);
ASSERT_FALSE(gethostname(&hostname[0], hostname.length()));
// trim null characters from string
hostname = std::string(hostname.c_str());
struct addrinfo hints, *info;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
ASSERT_FALSE(getaddrinfo(hostname.c_str(), "http", &hints, &info));
// Compare hostname part of FQDN
ASSERT_EQ(hostname, actual.substr(0, hostname.length()));
// Compare canonical name to FQDN
ASSERT_EQ(info->ai_canonname, actual);
freeaddrinfo(info);
}
@@ -0,0 +1,95 @@
//! @file test-getlinkcount.cpp
//! @author George Fleming <v-geflem@microsoft.com>
//! @brief Implements test for getLinkCount()
#include <gtest/gtest.h>
#include <pwd.h>
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include "getlinkcount.h"
class getLinkCountTest : public ::testing::Test
{
protected:
static const int bufSize = 64;
const std::string fileTemplate = "/tmp/createFile.XXXXXX";
char fileTemplateBuf[bufSize];
int32_t count;
char *file;
getLinkCountTest()
{
// since mkstemp modifies the template string, let's give it writable buffer
strcpy(fileTemplateBuf, fileTemplate.c_str());
int fd = mkstemp(fileTemplateBuf);
EXPECT_TRUE(fd != -1);
file = fileTemplateBuf;
}
void createFileForTesting(const std::string &theFile)
{
std::ofstream ofs;
ofs.open(theFile, std::ofstream::out);
ofs << "hi there, ms ostc!";
ofs.close();
}
std::string createHardLink(const std::string &origFile)
{
std::string newFile = origFile + "_link";
int ret = link(origFile.c_str(), newFile.c_str());
EXPECT_EQ(0, ret);
return newFile;
}
void removeFile(const std::string &fileName)
{
int ret = unlink(fileName.c_str());
EXPECT_EQ(0, ret);
}
};
TEST_F(getLinkCountTest, FilePathNameIsNull)
{
int32_t retVal = GetLinkCount(NULL, &count );
ASSERT_FALSE(retVal);
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
}
TEST_F(getLinkCountTest, FilePathNameDoesNotExist)
{
std::string invalidFile = "/tmp/createFile";
int32_t retVal = GetLinkCount(invalidFile.c_str(), &count);
ASSERT_FALSE(retVal);
EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno);
}
TEST_F(getLinkCountTest, LinkCountOfSinglyLinkedFile)
{
createFileForTesting(file);
int32_t retVal = GetLinkCount(file, &count);
ASSERT_TRUE(retVal);
EXPECT_EQ(1, count);
removeFile(file);
}
TEST_F(getLinkCountTest, LinkCountOfMultipliLinkedFile)
{
createFileForTesting(file);
std::string newFile = createHardLink(file);
int32_t retVal = GetLinkCount(file, &count);
ASSERT_TRUE(retVal);
EXPECT_EQ(2, count);
removeFile(file);
removeFile(newFile);
}
@@ -0,0 +1,17 @@
//! @file test-getusername.cpp
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
//! @brief Unit tests for GetUserName
#include <string>
#include <vector>
#include <unistd.h>
#include <gtest/gtest.h>
#include <pwd.h>
#include "getusername.h"
TEST(GetUserName, Success)
{
char* expected = getpwuid(geteuid())->pw_name;
ASSERT_TRUE(expected != NULL);
ASSERT_EQ(GetUserName(), std::string(expected));
}
@@ -0,0 +1,94 @@
//! @file test-isexecutable.cpp
//! @author George Fleming <v-geflem@microsoft.com>
//! @brief Implements test for isexecutable()
#include <gtest/gtest.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include "isexecutable.h"
using namespace std;
class IsExecutableTest : public ::testing::Test
{
protected:
static const int bufSize = 64;
const string fileTemplate = "/tmp/isexecutabletest.fXXXXXXX";
const mode_t mode_700 = S_IRUSR | S_IWUSR | S_IXUSR;
const mode_t mode_070 = S_IRGRP | S_IWGRP | S_IXGRP;
const mode_t mode_007 = S_IROTH | S_IWOTH | S_IXOTH;
const mode_t mode_777 = mode_700 | mode_070 | mode_007;
const mode_t mode_444 = S_IRUSR | S_IRGRP | S_IROTH;
char *file;
char fileTemplateBuf[bufSize];
IsExecutableTest()
{
// since mkstemp modifies the template string, let's give it writable buffers
strcpy(fileTemplateBuf, fileTemplate.c_str());
// First create a file
int fd = mkstemp(fileTemplateBuf);
EXPECT_TRUE(fd != -1);
file = fileTemplateBuf;
}
~IsExecutableTest()
{
int ret;
ret = unlink(file);
EXPECT_EQ(0, ret);
}
void ChangeFilePermission(const char* file, mode_t mode)
{
int ret = chmod(file, mode);
EXPECT_EQ(ret, 0);
}
};
TEST_F(IsExecutableTest, FilePathNameIsNull)
{
int32_t retVal = IsExecutable(NULL);
EXPECT_EQ(retVal, -1);
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
}
TEST_F(IsExecutableTest, FilePathNameDoesNotExist)
{
std::string invalidFile = "/tmp/isexecutabletest_invalidFile";
int32_t retVal = IsExecutable(invalidFile.c_str());
EXPECT_EQ(retVal, -1);
EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno);
}
TEST_F(IsExecutableTest, NormalFileIsNotIsexecutable)
{
int32_t retVal = IsExecutable(file);
EXPECT_EQ(0, retVal);
ChangeFilePermission(file, mode_444);
retVal = IsExecutable(file);
EXPECT_EQ(0, retVal);
}
TEST_F(IsExecutableTest, FilePermission_700)
{
ChangeFilePermission(file, mode_700);
int32_t retVal = IsExecutable(file);
EXPECT_EQ(1, retVal);
}
TEST_F(IsExecutableTest, FilePermission_777)
{
ChangeFilePermission(file, mode_777);
int32_t retVal = IsExecutable(file);
EXPECT_EQ(1, retVal);
}
+104
View File
@@ -0,0 +1,104 @@
//! @file test-issymlink.cpp
//! @author George Fleming <v-geflem@microsoft.com>
//! @brief Implements test for isSymLink()
#include <gtest/gtest.h>
#include <errno.h>
#include <unistd.h>
#include "issymlink.h"
using namespace std;
class isSymLinkTest : public ::testing::Test
{
protected:
static const int bufSize = 64;
const string fileTemplate = "/tmp/symlinktest.fXXXXXX";
const string dirTemplate = "/tmp/symlinktest.dXXXXXX";
const string fileSymLink = "/tmp/symlinktest.flink";
const string dirSymLink = "/tmp/symlinktest.dlink";
char *file, *dir;
char fileTemplateBuf[bufSize], dirTemplateBuf[bufSize];
isSymLinkTest()
{
// since mkstemp and mkdtemp modifies the template string, let's give them writable buffers
strcpy(fileTemplateBuf, fileTemplate.c_str());
strcpy(dirTemplateBuf, dirTemplate.c_str());
// First create a file
int fd = mkstemp(fileTemplateBuf);
EXPECT_TRUE(fd != -1);
file = fileTemplateBuf;
// Create a temp directory
dir = mkdtemp(dirTemplateBuf);
EXPECT_TRUE(dir != NULL);
// Create symbolic link to file
int ret1 = symlink(file, fileSymLink.c_str());
EXPECT_EQ(ret1, 0);
// Create symbolic link to directory
int ret2 = symlink(dir, dirSymLink.c_str());
EXPECT_EQ(ret2, 0);
}
~isSymLinkTest()
{
int ret;
ret = unlink(fileSymLink.c_str());
EXPECT_EQ(0, ret);
ret = unlink(dirSymLink.c_str());
EXPECT_EQ(0, ret);
ret = unlink(file);
EXPECT_EQ(0, ret);
ret = rmdir(dir);
EXPECT_EQ(0, ret);
}
};
TEST_F(isSymLinkTest, FilePathNameIsNull)
{
int retVal = IsSymLink(NULL);
EXPECT_EQ(retVal, -1);
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
}
TEST_F(isSymLinkTest, FilePathNameDoesNotExist)
{
std::string invalidFile = "/tmp/symlinktest_invalidFile";
int retVal = IsSymLink(invalidFile.c_str());
EXPECT_EQ(retVal, -1);
EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno);
}
TEST_F(isSymLinkTest, NormalFileIsNotSymLink)
{
int retVal = IsSymLink(file);
EXPECT_EQ(0, retVal);
}
TEST_F(isSymLinkTest, SymLinkToFile)
{
int retVal = IsSymLink(fileSymLink.c_str());
EXPECT_EQ(1, retVal);
}
TEST_F(isSymLinkTest, NormalDirectoryIsNotSymbLink)
{
int retVal = IsSymLink(dir);
EXPECT_EQ(0, retVal);
}
TEST_F(isSymLinkTest, SymLinkToDirectory)
{
int retVal = IsSymLink(dirSymLink.c_str());
EXPECT_EQ(1, retVal);
}
+21
View File
@@ -0,0 +1,21 @@
//! @file test-locale.cpp
//! @author Alex Jordan <v-alexjo@microsoft.com>
//! @brief Unit tests for linux locale
#include <gtest/gtest.h>
#include <langinfo.h>
#include <locale.h>
#include <string>
#include <stdio.h>
//! Test fixture for LocaleTest
class LocaleTest : public ::testing::Test
{
};
TEST_F(LocaleTest, Success)
{
setlocale (LC_ALL, "");
ASSERT_FALSE (nl_langinfo(CODESET) == NULL);
ASSERT_TRUE(nl_langinfo(CODESET) == std::string("UTF-8"));
}