sfx2 store: avoid rename() for files with >1 hard link count
Rename would break the hard link; in practice e.g. Online's 'make run'
debug feature depends on not breaking hardlinks.
Change-Id: Id3ac19493b6b473274b7ed53a4ca06fc34bc1668
Reviewed-on: https://gerrit.libreoffice.org/51858
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
Tested-by: Jenkins <ci@libreoffice.org>
diff --git a/sfx2/qa/cppunit/test_misc.cxx b/sfx2/qa/cppunit/test_misc.cxx
index a1ecc01..72f21fc 100644
--- a/sfx2/qa/cppunit/test_misc.cxx
+++ b/sfx2/qa/cppunit/test_misc.cxx
@@ -11,6 +11,7 @@
#ifndef _WIN32
#include <sys/stat.h>
#include <unistd.h>
#endif
#include <memory>
@@ -51,6 +52,7 @@ public:
virtual void setUp() override;
void testODFCustomMetadata();
void testNoThumbnail();
void testHardLinks();
virtual void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override
{
@@ -66,6 +68,7 @@ public:
CPPUNIT_TEST_SUITE(MiscTest);
CPPUNIT_TEST(testODFCustomMetadata);
CPPUNIT_TEST(testNoThumbnail);
CPPUNIT_TEST(testHardLinks);
CPPUNIT_TEST_SUITE_END();
private:
@@ -147,6 +150,35 @@ void MiscTest::testNoThumbnail()
xComponent->dispose();
}
void MiscTest::testHardLinks()
{
#ifndef _WIN32
OUString aSourceDir = m_directories.getURLFromSrc("/sfx2/qa/cppunit/misc/");
OUString aTargetDir = m_directories.getURLFromWorkdir("/CppunitTest/sfx2_misc.test.user/");
const OUString aURL(aTargetDir + "hello.odt");
osl::File::copy(aSourceDir + "hello.odt", aURL);
OUString aTargetPath;
osl::FileBase::getSystemPathFromFileURL(aURL, aTargetPath);
OString aOld = aTargetPath.toUtf8();
aTargetPath += ".2";
OString aNew = aTargetPath.toUtf8();
link(aOld.getStr(), aNew.getStr());
uno::Reference<lang::XComponent> xComponent = loadFromDesktop(aURL, "com.sun.star.text.TextDocument");
CPPUNIT_ASSERT(xComponent.is());
uno::Reference<frame::XStorable> xStorable(xComponent, uno::UNO_QUERY);
xStorable->store();
struct stat buf;
stat(aOld.getStr(), &buf);
// This failed: hard link count was 1, the hard link broke on store.
CPPUNIT_ASSERT(buf.st_nlink > 1);
xComponent->dispose();
#endif
}
CPPUNIT_TEST_SUITE_REGISTRATION(MiscTest);
}
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
index 63ce058..d3f78d2 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -19,6 +19,10 @@
#include <config_features.h>
#ifdef UNX
#include <sys/stat.h>
#endif
#include <sfx2/docfile.hxx>
#include <sfx2/signaturestate.hxx>
@@ -190,6 +194,28 @@ sal_uInt64 GetDefaultFileAttributes(const OUString& rURL)
return nRet;
}
/// Determines if rURL is a non-hard-linked file:// URL.
bool IsNotHardLinkedFile(const OUString& rURL)
{
if (!comphelper::isFileUrl(rURL))
return false;
#ifdef UNX
OUString rPath;
if (osl::FileBase::getSystemPathFromFileURL(rURL, rPath) != osl::FileBase::E_None)
return false;
struct stat buf;
if (stat(rPath.toUtf8().getStr(), &buf) != 0)
return false;
if (buf.st_nlink > 1)
return false;
#endif
return true;
}
} // anonymous namespace
class SfxMedium_Impl
@@ -1812,7 +1838,7 @@ void SfxMedium::TransactedTransferForFS_Impl( const INetURLObject& aSource,
OUString aDestMainURL = aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE);
sal_uInt64 nAttributes = GetDefaultFileAttributes(aDestMainURL);
if (comphelper::isFileUrl(aDestMainURL) && osl::File::move(aSourceMainURL, aDestMainURL) == osl::FileBase::E_None)
if (IsNotHardLinkedFile(aDestMainURL) && osl::File::move(aSourceMainURL, aDestMainURL) == osl::FileBase::E_None)
{
if (nAttributes)
// Adjust attributes, source might be created with