-
Notifications
You must be signed in to change notification settings - Fork 807
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add the XAttr backend prototype with its test suite
Signed-off-by: Kevin Ottens <[email protected]>
- Loading branch information
Kevin Ottens
committed
Jan 14, 2021
1 parent
7c8b7db
commit 1aeb77c
Showing
7 changed files
with
1,454 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/* | ||
* Copyright (C) by Kevin Ottens <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, but | ||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* for more details. | ||
*/ | ||
|
||
#include "vfs_xattr.h" | ||
|
||
#include <QFile> | ||
|
||
#include "syncfileitem.h" | ||
#include "filesystem.h" | ||
#include "common/syncjournaldb.h" | ||
|
||
#include "xattrwrapper.h" | ||
|
||
namespace xattr { | ||
using namespace OCC::XAttrWrapper; | ||
} | ||
|
||
namespace OCC { | ||
|
||
VfsXAttr::VfsXAttr(QObject *parent) | ||
: Vfs(parent) | ||
{ | ||
} | ||
|
||
VfsXAttr::~VfsXAttr() = default; | ||
|
||
Vfs::Mode VfsXAttr::mode() const | ||
{ | ||
return XAttr; | ||
} | ||
|
||
QString VfsXAttr::fileSuffix() const | ||
{ | ||
return QString(); | ||
} | ||
|
||
void VfsXAttr::startImpl(const VfsSetupParams &) | ||
{ | ||
} | ||
|
||
void VfsXAttr::stop() | ||
{ | ||
} | ||
|
||
void VfsXAttr::unregisterFolder() | ||
{ | ||
} | ||
|
||
bool VfsXAttr::socketApiPinStateActionsShown() const | ||
{ | ||
return true; | ||
} | ||
|
||
bool VfsXAttr::isHydrating() const | ||
{ | ||
return false; | ||
} | ||
|
||
Result<void, QString> VfsXAttr::updateMetadata(const QString &filePath, time_t modtime, qint64, const QByteArray &) | ||
{ | ||
FileSystem::setModTime(filePath, modtime); | ||
return {}; | ||
} | ||
|
||
Result<void, QString> VfsXAttr::createPlaceholder(const SyncFileItem &item) | ||
{ | ||
const auto path = QString(_setupParams.filesystemPath + item._file); | ||
QFile file(path); | ||
if (file.exists() && file.size() > 1 | ||
&& !FileSystem::verifyFileUnchanged(path, item._size, item._modtime)) { | ||
return QStringLiteral("Cannot create a placeholder because a file with the placeholder name already exist"); | ||
} | ||
|
||
if (!file.open(QFile::ReadWrite | QFile::Truncate)) { | ||
return file.errorString(); | ||
} | ||
|
||
file.write(" "); | ||
file.close(); | ||
FileSystem::setModTime(path, item._modtime); | ||
return xattr::addNextcloudPlaceholderAttributes(path); | ||
} | ||
|
||
Result<void, QString> VfsXAttr::dehydratePlaceholder(const SyncFileItem &item) | ||
{ | ||
const auto path = QString(_setupParams.filesystemPath + item._file); | ||
QFile file(path); | ||
if (!file.remove()) { | ||
return QStringLiteral("Couldn't remove the original file to dehydrate"); | ||
} | ||
auto r = createPlaceholder(item); | ||
if (!r) { | ||
return r; | ||
} | ||
|
||
// Ensure the pin state isn't contradictory | ||
const auto pin = pinState(item._file); | ||
if (pin && *pin == PinState::AlwaysLocal) { | ||
setPinState(item._renameTarget, PinState::Unspecified); | ||
} | ||
return {}; | ||
} | ||
|
||
Result<void, QString> VfsXAttr::convertToPlaceholder(const QString &, const SyncFileItem &, const QString &) | ||
{ | ||
// Nothing necessary | ||
return {}; | ||
} | ||
|
||
bool VfsXAttr::needsMetadataUpdate(const SyncFileItem &) | ||
{ | ||
return false; | ||
} | ||
|
||
bool VfsXAttr::isDehydratedPlaceholder(const QString &filePath) | ||
{ | ||
const auto fi = QFileInfo(filePath); | ||
return fi.exists() && | ||
xattr::hasNextcloudPlaceholderAttributes(filePath); | ||
} | ||
|
||
bool VfsXAttr::statTypeVirtualFile(csync_file_stat_t *stat, void *statData) | ||
{ | ||
if (stat->type == ItemTypeDirectory) { | ||
return false; | ||
} | ||
|
||
const auto parentPath = static_cast<QByteArray *>(statData); | ||
Q_ASSERT(!parentPath->endsWith('/')); | ||
Q_ASSERT(!stat->path.startsWith('/')); | ||
|
||
const auto path = QByteArray(*parentPath + '/' + stat->path); | ||
const auto pin = [=] { | ||
const auto absolutePath = QString::fromUtf8(path); | ||
Q_ASSERT(absolutePath.startsWith(params().filesystemPath.toUtf8())); | ||
const auto folderPath = absolutePath.mid(params().filesystemPath.length()); | ||
return pinState(folderPath); | ||
}(); | ||
|
||
if (xattr::hasNextcloudPlaceholderAttributes(path)) { | ||
const auto shouldDownload = pin && (*pin == PinState::AlwaysLocal); | ||
stat->type = shouldDownload ? ItemTypeVirtualFileDownload : ItemTypeVirtualFile; | ||
return true; | ||
} else { | ||
const auto shouldDehydrate = pin && (*pin == PinState::OnlineOnly); | ||
if (shouldDehydrate) { | ||
stat->type = ItemTypeVirtualFileDehydration; | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
bool VfsXAttr::setPinState(const QString &folderPath, PinState state) | ||
{ | ||
return setPinStateInDb(folderPath, state); | ||
} | ||
|
||
Optional<PinState> VfsXAttr::pinState(const QString &folderPath) | ||
{ | ||
return pinStateInDb(folderPath); | ||
} | ||
|
||
Vfs::AvailabilityResult VfsXAttr::availability(const QString &folderPath) | ||
{ | ||
return availabilityInDb(folderPath); | ||
} | ||
|
||
void VfsXAttr::fileStatusChanged(const QString &, SyncFileStatus) | ||
{ | ||
} | ||
|
||
} // namespace OCC | ||
|
||
OCC_DEFINE_VFS_FACTORY("xattr", OCC::VfsXAttr) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright (C) by Kevin Ottens <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, but | ||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* for more details. | ||
*/ | ||
#pragma once | ||
|
||
#include <QObject> | ||
#include <QScopedPointer> | ||
|
||
#include "common/vfs.h" | ||
|
||
namespace OCC { | ||
|
||
class VfsXAttr : public Vfs | ||
{ | ||
Q_OBJECT | ||
|
||
public: | ||
explicit VfsXAttr(QObject *parent = nullptr); | ||
~VfsXAttr(); | ||
|
||
Mode mode() const override; | ||
QString fileSuffix() const override; | ||
|
||
void stop() override; | ||
void unregisterFolder() override; | ||
|
||
bool socketApiPinStateActionsShown() const override; | ||
bool isHydrating() const override; | ||
|
||
Result<void, QString> updateMetadata(const QString &filePath, time_t modtime, qint64 size, const QByteArray &fileId) override; | ||
|
||
Result<void, QString> createPlaceholder(const SyncFileItem &item) override; | ||
Result<void, QString> dehydratePlaceholder(const SyncFileItem &item) override; | ||
Result<void, QString> convertToPlaceholder(const QString &filename, const SyncFileItem &item, const QString &replacesFile) override; | ||
|
||
bool needsMetadataUpdate(const SyncFileItem &item) override; | ||
bool isDehydratedPlaceholder(const QString &filePath) override; | ||
bool statTypeVirtualFile(csync_file_stat_t *stat, void *statData) override; | ||
|
||
bool setPinState(const QString &folderPath, PinState state) override; | ||
Optional<PinState> pinState(const QString &folderPath) override; | ||
AvailabilityResult availability(const QString &folderPath) override; | ||
|
||
public slots: | ||
void fileStatusChanged(const QString &systemFileName, SyncFileStatus fileStatus) override; | ||
|
||
protected: | ||
void startImpl(const VfsSetupParams ¶ms) override; | ||
}; | ||
|
||
} // namespace OCC |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* Copyright (C) by Kevin Ottens <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, but | ||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* for more details. | ||
*/ | ||
#pragma once | ||
|
||
#include <QString> | ||
|
||
#include "owncloudlib.h" | ||
#include "common/result.h" | ||
|
||
namespace OCC { | ||
|
||
namespace XAttrWrapper | ||
{ | ||
|
||
OWNCLOUDSYNC_EXPORT bool hasNextcloudPlaceholderAttributes(const QString &path); | ||
OWNCLOUDSYNC_EXPORT Result<void, QString> addNextcloudPlaceholderAttributes(const QString &path); | ||
|
||
} | ||
|
||
} // namespace OCC |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* | ||
* Copyright (C) by Kevin Ottens <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, but | ||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* for more details. | ||
*/ | ||
|
||
#include "xattrwrapper.h" | ||
|
||
#include "config.h" | ||
|
||
#include <QLoggingCategory> | ||
|
||
#include <sys/xattr.h> | ||
|
||
Q_LOGGING_CATEGORY(lcXAttrWrapper, "nextcloud.sync.vfs.xattr.wrapper", QtInfoMsg) | ||
|
||
namespace { | ||
constexpr auto hydrateExecAttributeName = "user.nextcloud.hydrate_exec"; | ||
|
||
OCC::Optional<QByteArray> xattrGet(const QByteArray &path, const QByteArray &name) | ||
{ | ||
constexpr auto bufferSize = 256; | ||
QByteArray result; | ||
result.resize(bufferSize); | ||
const auto count = getxattr(path.constData(), name.constData(), result.data(), bufferSize); | ||
if (count >= 0) { | ||
result.resize(static_cast<int>(count) - 1); | ||
return result; | ||
} else { | ||
return {}; | ||
} | ||
} | ||
|
||
bool xattrSet(const QByteArray &path, const QByteArray &name, const QByteArray &value) | ||
{ | ||
const auto returnCode = setxattr(path.constData(), name.constData(), value.constData(), value.size() + 1, 0); | ||
return returnCode == 0; | ||
} | ||
|
||
} | ||
|
||
|
||
bool OCC::XAttrWrapper::hasNextcloudPlaceholderAttributes(const QString &path) | ||
{ | ||
const auto value = xattrGet(path.toUtf8(), hydrateExecAttributeName); | ||
if (value) { | ||
return *value == QByteArrayLiteral(APPLICATION_EXECUTABLE); | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
OCC::Result<void, QString> OCC::XAttrWrapper::addNextcloudPlaceholderAttributes(const QString &path) | ||
{ | ||
const auto success = xattrSet(path.toUtf8(), hydrateExecAttributeName, APPLICATION_EXECUTABLE); | ||
if (!success) { | ||
return QStringLiteral("Failed to set the extended attribute"); | ||
} else { | ||
return {}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.