From 43c7bdd137c7bde591727ceea621f8746d9bf026 Mon Sep 17 00:00:00 2001 From: George Fleming Date: Fri, 23 Oct 2015 10:55:55 -0700 Subject: [PATCH 1/3] New GetLinkCount() --- CMakeLists.txt | 2 +- impl/getlinkcount.cpp | 120 ++++++++++++++++++++++++++++++++++++++++++ impl/getlinkcount.h | 9 ++++ impl/pal.h | 7 +++ 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 impl/getlinkcount.cpp create mode 100644 impl/getlinkcount.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b534ebb94..6b8b426dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include_directories(../ext-src/gtest/fused-src impl) link_directories(${monad_native_BINARY_DIR}) # source file definitions -set(LIB_SOURCE_FILES impl/getcurrentprocessorid.cpp impl/getusername.cpp impl/terminal.cpp impl/getcomputername.cpp impl/getcpinfo.cpp) +set(LIB_SOURCE_FILES impl/getcurrentprocessorid.cpp impl/getusername.cpp impl/terminal.cpp impl/getcomputername.cpp impl/getcpinfo.cpp impl/getlinkcount.cpp) set(HOST_COMMON_SOURCE_FILES host/common/coreclrutil.cpp) set(HOST_COMMON_TEST_SOURCE_FILES tests/host/test-hostutil.cpp) set(TEST_SOURCE_FILES tests/test-getcurrentprocessid.cpp tests/test-getusername.cpp tests/test-getcomputername.cpp tests/test-getcpinfo.cpp ${HOST_COMMON_SOURCE_FILES} ${HOST_COMMON_TEST_SOURCE_FILES}) diff --git a/impl/getlinkcount.cpp b/impl/getlinkcount.cpp new file mode 100644 index 000000000..1af39b69c --- /dev/null +++ b/impl/getlinkcount.cpp @@ -0,0 +1,120 @@ +//! @file getlinkcount.cpp +//! @author George FLeming +//! @brief Retrieve link count of a file + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "getlinkcount.h" + +//! @brief GetLinkCount retrieves the file link count (number of hard links) +//! for the given file +//! +//! GetLinkCountW is the Unicode variation. See [MSDN documentation]. +//! +//! @param[in] fileName +//! @parblock +//! A pointer to the buffer that contains the file name +//! +//! WCHAR_T* is a Unicode [LPTSTR]. +//! @endparblock +//! +//! @param[out] count +//! @parblock +//! This function returns the number of hard links associated with this file +//! +//! TCHAR is a Unicode 16-bit [WCHAR]. +//! +//! @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 TRUE If the function succeeds, the return value is a nonzero +//! value, and the variable pointed to by buffer contains infomation about the files +//! @retval FALSE If the function fails, the return value is zero. To get +//! extended error information, call GetLastError. +//! + +BOOL GetLinkCountW(char* fileName, LPDWORD count) +{ + errno = 0; + + // Check parameters + if (!fileName) + { + errno = ERROR_INVALID_PARAMETER; + return FALSE; + } + + // 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 FALSE; + } + + 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 FALSE; + } + + *count = statBuf.st_nlink; + return TRUE; + +} + diff --git a/impl/getlinkcount.h b/impl/getlinkcount.h new file mode 100644 index 000000000..47243c969 --- /dev/null +++ b/impl/getlinkcount.h @@ -0,0 +1,9 @@ +#pragma once + +#include "pal.h" + +PAL_BEGIN_EXTERNC + +BOOL GetLinkCountW(char* fileName, LPDWORD count); + +PAL_END_EXTERNC diff --git a/impl/pal.h b/impl/pal.h index 944336ab0..c53c985a8 100644 --- a/impl/pal.h +++ b/impl/pal.h @@ -82,6 +82,13 @@ #define MAX_PATH 0x00000104 #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 + typedef unsigned long long uint64; #endif From cf9b390faaddb2306030bb2cfdcef15b27b3f722 Mon Sep 17 00:00:00 2001 From: George Fleming Date: Fri, 23 Oct 2015 15:48:48 -0700 Subject: [PATCH 2/3] Adding native tests --- CMakeLists.txt | 2 +- tests/test-getlinkcount.cpp | 90 +++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 tests/test-getlinkcount.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b8b426dd..034bb2a89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ link_directories(${monad_native_BINARY_DIR}) set(LIB_SOURCE_FILES impl/getcurrentprocessorid.cpp impl/getusername.cpp impl/terminal.cpp impl/getcomputername.cpp impl/getcpinfo.cpp impl/getlinkcount.cpp) set(HOST_COMMON_SOURCE_FILES host/common/coreclrutil.cpp) set(HOST_COMMON_TEST_SOURCE_FILES tests/host/test-hostutil.cpp) -set(TEST_SOURCE_FILES tests/test-getcurrentprocessid.cpp tests/test-getusername.cpp tests/test-getcomputername.cpp tests/test-getcpinfo.cpp ${HOST_COMMON_SOURCE_FILES} ${HOST_COMMON_TEST_SOURCE_FILES}) +set(TEST_SOURCE_FILES tests/test-getcurrentprocessid.cpp tests/test-getusername.cpp tests/test-getcomputername.cpp tests/test-getcpinfo.cpp tests/test-getlinkcount.cpp ${HOST_COMMON_SOURCE_FILES} ${HOST_COMMON_TEST_SOURCE_FILES}) set(SOURCE_FILES main.cpp ../ext-src/gtest/fused-src/gtest/gtest-all.cc) set(HOST_CMDLINE_SOURCE_FILES host/cmdline/main.cpp) diff --git a/tests/test-getlinkcount.cpp b/tests/test-getlinkcount.cpp new file mode 100644 index 000000000..081b0474e --- /dev/null +++ b/tests/test-getlinkcount.cpp @@ -0,0 +1,90 @@ +//! @file test-getlinkcount.cpp +//! @author George Fleming +//! @brief Implements test for getLinkCount() + +#include +#include +#include +#include +#include +#include +#include +#include "getlinkcount.h" + +class getLinkCountTest : public ::testing::Test +{ + protected: + + std::string file; + const std::string fileTemplate = "/tmp/createFile.XXXXXXX"; + ulong count; + + getLinkCountTest() + { + file = fileTemplate; + file = mktemp(const_cast(file.c_str())); + std::ifstream fileCondition(file); + EXPECT_EQ(0, fileCondition.good()); + } + + void createFileForTesting(std::string theFile) + { + std::ofstream ofs; + ofs.open(theFile, std::ofstream::out); + ofs << "hi there, ms ostc!"; + ofs.close(); + } + + std::string createHardLink(std::string origFile) + { + std::string newFile = origFile + "_link"; + int ret = link(const_cast(origFile.c_str()), const_cast(newFile.c_str())); + EXPECT_EQ(0, ret); + + return newFile; + } + + void removeFile(std::string fileName) + { + int ret = unlink(const_cast(fileName.c_str())); + EXPECT_EQ(0, ret); + } +}; + +TEST_F(getLinkCountTest, FilePathNameIsNull) +{ + bool retVal = GetLinkCountW(NULL, &count ); + ASSERT_FALSE(retVal); + EXPECT_EQ(ERROR_INVALID_PARAMETER, errno); +} + +TEST_F(getLinkCountTest, FilePathNameDoesNotExist) +{ + std::string invalidFile = "/tmp/createFile"; + bool retVal = GetLinkCountW(const_cast(invalidFile.c_str()), &count); + ASSERT_FALSE(retVal); + EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno); +} + +TEST_F(getLinkCountTest, LinkCountOfSinglyLinkedFile) +{ + createFileForTesting(file); + bool retVal = GetLinkCountW(const_cast(file.c_str()), &count); + ASSERT_TRUE(retVal); + EXPECT_EQ(1, count); + + removeFile(file); +} + +TEST_F(getLinkCountTest, LinkCountOfMultipliLinkedFile) +{ + createFileForTesting(file); + std::string newFile = createHardLink(file); + bool retVal = GetLinkCountW(const_cast(file.c_str()), &count); + ASSERT_TRUE(retVal); + EXPECT_EQ(2, count); + + removeFile(file); + removeFile(newFile); +} + From b91c1173137881f7cb478e758f31388569d04af8 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Sat, 24 Oct 2015 21:30:42 -0700 Subject: [PATCH 3/3] GetLinkCount is not Unicode --- impl/getlinkcount.cpp | 10 ++++------ impl/getlinkcount.h | 2 +- tests/test-getlinkcount.cpp | 8 ++++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/impl/getlinkcount.cpp b/impl/getlinkcount.cpp index 1af39b69c..2dde56bba 100644 --- a/impl/getlinkcount.cpp +++ b/impl/getlinkcount.cpp @@ -16,20 +16,19 @@ //! @brief GetLinkCount retrieves the file link count (number of hard links) //! for the given file //! -//! GetLinkCountW is the Unicode variation. See [MSDN documentation]. +//! GetLinkCount //! //! @param[in] fileName //! @parblock //! A pointer to the buffer that contains the file name //! -//! WCHAR_T* is a Unicode [LPTSTR]. +//! 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 -//! -//! TCHAR is a Unicode 16-bit [WCHAR]. +//! @endparblock //! //! @exception errno Passes these errors via errno to GetLastError: //! - ERROR_INVALID_PARAMETER: parameter is not valid @@ -52,7 +51,7 @@ //! extended error information, call GetLastError. //! -BOOL GetLinkCountW(char* fileName, LPDWORD count) +BOOL GetLinkCount(char* fileName, LPDWORD count) { errno = 0; @@ -117,4 +116,3 @@ BOOL GetLinkCountW(char* fileName, LPDWORD count) return TRUE; } - diff --git a/impl/getlinkcount.h b/impl/getlinkcount.h index 47243c969..738a606c6 100644 --- a/impl/getlinkcount.h +++ b/impl/getlinkcount.h @@ -4,6 +4,6 @@ PAL_BEGIN_EXTERNC -BOOL GetLinkCountW(char* fileName, LPDWORD count); +BOOL GetLinkCount(char* fileName, LPDWORD count); PAL_END_EXTERNC diff --git a/tests/test-getlinkcount.cpp b/tests/test-getlinkcount.cpp index 081b0474e..0c7a3d0c5 100644 --- a/tests/test-getlinkcount.cpp +++ b/tests/test-getlinkcount.cpp @@ -53,7 +53,7 @@ class getLinkCountTest : public ::testing::Test TEST_F(getLinkCountTest, FilePathNameIsNull) { - bool retVal = GetLinkCountW(NULL, &count ); + bool retVal = GetLinkCount(NULL, &count ); ASSERT_FALSE(retVal); EXPECT_EQ(ERROR_INVALID_PARAMETER, errno); } @@ -61,7 +61,7 @@ TEST_F(getLinkCountTest, FilePathNameIsNull) TEST_F(getLinkCountTest, FilePathNameDoesNotExist) { std::string invalidFile = "/tmp/createFile"; - bool retVal = GetLinkCountW(const_cast(invalidFile.c_str()), &count); + bool retVal = GetLinkCount(const_cast(invalidFile.c_str()), &count); ASSERT_FALSE(retVal); EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno); } @@ -69,7 +69,7 @@ TEST_F(getLinkCountTest, FilePathNameDoesNotExist) TEST_F(getLinkCountTest, LinkCountOfSinglyLinkedFile) { createFileForTesting(file); - bool retVal = GetLinkCountW(const_cast(file.c_str()), &count); + bool retVal = GetLinkCount(const_cast(file.c_str()), &count); ASSERT_TRUE(retVal); EXPECT_EQ(1, count); @@ -80,7 +80,7 @@ TEST_F(getLinkCountTest, LinkCountOfMultipliLinkedFile) { createFileForTesting(file); std::string newFile = createHardLink(file); - bool retVal = GetLinkCountW(const_cast(file.c_str()), &count); + bool retVal = GetLinkCount(const_cast(file.c_str()), &count); ASSERT_TRUE(retVal); EXPECT_EQ(2, count);