From 2de4fe9be6ad791a5e406bf98efd7ec3d5258c01 Mon Sep 17 00:00:00 2001 From: George Fleming Date: Fri, 30 Oct 2015 11:47:44 -0700 Subject: [PATCH] Adding new isSymLink() function --- native/CMakeLists.txt | 1 + native/issymlink.cpp | 108 +++++++++++++++++++++++++++++++++++++++ native/issymlink.h | 9 ++++ tests/CMakeLists.txt | 1 + tests/test-issymlink.cpp | 104 +++++++++++++++++++++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 native/issymlink.cpp create mode 100644 native/issymlink.h create mode 100644 tests/test-issymlink.cpp diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index 76682aa84..1b57c13d2 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(psnative SHARED getcomputername.cpp getlinkcount.cpp getfullyqualifiedname.cpp + issymlink.cpp terminal.cpp) target_link_libraries(psnative icuuc) diff --git a/native/issymlink.cpp b/native/issymlink.cpp new file mode 100644 index 000000000..0d3a1f387 --- /dev/null +++ b/native/issymlink.cpp @@ -0,0 +1,108 @@ +//! @file isSymLink.cpp +//! @author George FLeming +//! @brief returns whether a path is a symbolic link + +#include +#include +#include +#include +#include +#include +#include +#include +#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_BAD_ENVIRONMENT: locale is not UTF-8 +//! - 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. +//! + +long IsSymLink(const char* fileName) +{ + errno = 0; + + // Check parameters + if (!fileName) + { + errno = ERROR_INVALID_PARAMETER; + return -1; + } + + // 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 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; +} diff --git a/native/issymlink.h b/native/issymlink.h new file mode 100644 index 000000000..3ba5e48ac --- /dev/null +++ b/native/issymlink.h @@ -0,0 +1,9 @@ +#pragma once + +#include "pal.h" + +PAL_BEGIN_EXTERNC + +long IsSymLink(const char* fileName); + +PAL_END_EXTERNC diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3dfb4fdd4..b9bba62a2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(psnative-test test-getcomputername.cpp test-getlinkcount.cpp test-getfullyqualifiedname.cpp + test-issymlink.cpp main.cpp) # manually include gtest headers diff --git a/tests/test-issymlink.cpp b/tests/test-issymlink.cpp new file mode 100644 index 000000000..41515fbc3 --- /dev/null +++ b/tests/test-issymlink.cpp @@ -0,0 +1,104 @@ +//! @file test-issymlink.cpp +//! @author George Fleming +//! @brief Implements test for isSymLink() + +#include +#include +#include +#include "issymlink.h" + +using namespace std; + +class isSymLinkTest : public ::testing::Test +{ +protected: + + static const int bufSize = 64; + const string fileTemplate = "/tmp/symlinktest.fXXXXXXX"; + const string dirTemplate = "/tmp/symlinktest.dXXXXXXX"; + 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); +} +