diff options
-rw-r--r-- | lib/win32/emu.cpp | 68 | ||||
-rw-r--r-- | lib/win32/emu.h | 2 | ||||
-rw-r--r-- | test/bbackupd/testbbackupd.cpp | 34 |
3 files changed, 104 insertions, 0 deletions
diff --git a/lib/win32/emu.cpp b/lib/win32/emu.cpp index fb8fb437..ee1f461a 100644 --- a/lib/win32/emu.cpp +++ b/lib/win32/emu.cpp @@ -1667,6 +1667,73 @@ int emu_mkdir(const char* pPathName) return 0; } +int emu_link(const char* pOldPath, const char* pNewPath) +{ + std::string AbsOldPathWithUnicode = + ConvertPathToAbsoluteUnicode(pOldPath); + + if (AbsOldPathWithUnicode.size() == 0) + { + // error already logged by ConvertPathToAbsoluteUnicode() + return -1; + } + + std::string AbsNewPathWithUnicode = + ConvertPathToAbsoluteUnicode(pNewPath); + + if (AbsNewPathWithUnicode.size() == 0) + { + // error already logged by ConvertPathToAbsoluteUnicode() + return -1; + } + + WCHAR* pOldBuffer = ConvertUtf8ToWideString(AbsOldPathWithUnicode.c_str()); + if (!pOldBuffer) + { + return -1; + } + + WCHAR* pNewBuffer = ConvertUtf8ToWideString(AbsNewPathWithUnicode.c_str()); + if (!pNewBuffer) + { + delete [] pOldBuffer; + return -1; + } + + BOOL result = CreateHardLinkW(pNewBuffer, pOldBuffer, NULL); + DWORD err = GetLastError(); + delete [] pOldBuffer; + delete [] pNewBuffer; + + if (!result) + { + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) + { + errno = ENOENT; + } + else if (err == ERROR_SHARING_VIOLATION) + { + errno = EBUSY; + } + else if (err == ERROR_ACCESS_DENIED) + { + errno = EACCES; + } + else + { + ::syslog(LOG_WARNING, "Failed to hardlink file " + "'%s' to '%s': %s", pOldPath, pNewPath, + GetErrorMessage(err).c_str()); + errno = ENOSYS; + } + + return -1; + } + + return 0; + +} + int emu_unlink(const char* pFileName) { std::string AbsPathWithUnicode = @@ -1709,6 +1776,7 @@ int emu_unlink(const char* pFileName) GetErrorMessage(err).c_str()); errno = ENOSYS; } + return -1; } diff --git a/lib/win32/emu.h b/lib/win32/emu.h index c28b0bdb..05f2f561 100644 --- a/lib/win32/emu.h +++ b/lib/win32/emu.h @@ -353,6 +353,7 @@ bool ConvertTime_tToFileTime(const time_t from, FILETIME *pTo); int emu_chdir (const char* pDirName); int emu_mkdir (const char* pPathName); +int emu_link (const char* pOldPath, const char* pNewPath); int emu_unlink (const char* pFileName); int emu_fstat (HANDLE file, struct emu_stat* st); int emu_stat (const char* pName, struct emu_stat* st); @@ -363,6 +364,7 @@ int emu_rename (const char* pOldName, const char* pNewName); #define chdir(directory) emu_chdir (directory) #define mkdir(path, mode) emu_mkdir (path) +#define link(oldpath, newpath) emu_link (oldpath, newpath) #define unlink(file) emu_unlink (file) #define utimes(buffer, times) emu_utimes (buffer, times) #define chmod(file, mode) emu_chmod (file, mode) diff --git a/test/bbackupd/testbbackupd.cpp b/test/bbackupd/testbbackupd.cpp index 25b00ef5..fa04c848 100644 --- a/test/bbackupd/testbbackupd.cpp +++ b/test/bbackupd/testbbackupd.cpp @@ -1742,6 +1742,39 @@ bool test_ssl_keepalives() TEARDOWN(); } +bool test_backup_hardlinked_files() +{ + SETUP_WITH_BBSTORED(); + + bbackupd.RunSyncNow(); + TEST_COMPARE(Compare_Same); + + // Create some hard links. First in the same directory: + TEST_THAT(link("testfiles/TestDir1/x1/dsfdsfs98.fd", + "testfiles/TestDir1/x1/hardlink1") == 0); + bbackupd.RunSyncNow(); + TEST_COMPARE(Compare_Same); + + // Now in a different directory + TEST_THAT(mkdir("testfiles/TestDir1/x2", 0755) == 0); + TEST_THAT(link("testfiles/TestDir1/x1/dsfdsfs98.fd", + "testfiles/TestDir1/x2/hardlink2") == 0); + bbackupd.RunSyncNow(); + TEST_COMPARE(Compare_Same); + + // Now delete one of them + TEST_THAT(unlink("testfiles/TestDir1/x1/dsfdsfs98.fd") == 0); + bbackupd.RunSyncNow(); + TEST_COMPARE(Compare_Same); + + // And another. + TEST_THAT(unlink("testfiles/TestDir1/x1/hardlink1") == 0); + bbackupd.RunSyncNow(); + TEST_COMPARE(Compare_Same); + + TEARDOWN(); +} + bool test_backup_pauses_when_store_is_full() { SETUP_WITHOUT_FILES(); @@ -4202,6 +4235,7 @@ int test(int argc, const char *argv[]) // TEST_THAT(test_replace_zero_byte_file_with_nonzero_byte_file()); TEST_THAT(test_backup_disappearing_directory()); TEST_THAT(test_ssl_keepalives()); + TEST_THAT(test_backup_hardlinked_files()); TEST_THAT(test_backup_pauses_when_store_is_full()); TEST_THAT(test_bbackupd_exclusions()); TEST_THAT(test_bbackupd_uploads_files()); |