summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/win32/emu.cpp68
-rw-r--r--lib/win32/emu.h2
-rw-r--r--test/bbackupd/testbbackupd.cpp34
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());