1 /*
2 * Copyright (C) by Kevin Ottens <kevin.ottens@nextcloud.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15 #include "vfs_xattr.h"
16
17 #include <QFile>
18
19 #include "syncfileitem.h"
20 #include "filesystem.h"
21 #include "common/syncjournaldb.h"
22
23 #include "xattrwrapper.h"
24
25 namespace xattr {
26 using namespace OCC::XAttrWrapper;
27 }
28
29 namespace OCC {
30
VfsXAttr(QObject * parent)31 VfsXAttr::VfsXAttr(QObject *parent)
32 : Vfs(parent)
33 {
34 }
35
36 VfsXAttr::~VfsXAttr() = default;
37
mode() const38 Vfs::Mode VfsXAttr::mode() const
39 {
40 return XAttr;
41 }
42
fileSuffix() const43 QString VfsXAttr::fileSuffix() const
44 {
45 return QString();
46 }
47
startImpl(const VfsSetupParams &)48 void VfsXAttr::startImpl(const VfsSetupParams &)
49 {
50 }
51
stop()52 void VfsXAttr::stop()
53 {
54 }
55
unregisterFolder()56 void VfsXAttr::unregisterFolder()
57 {
58 }
59
socketApiPinStateActionsShown() const60 bool VfsXAttr::socketApiPinStateActionsShown() const
61 {
62 return true;
63 }
64
isHydrating() const65 bool VfsXAttr::isHydrating() const
66 {
67 return false;
68 }
69
updateMetadata(const QString & filePath,time_t modtime,qint64,const QByteArray &)70 Result<void, QString> VfsXAttr::updateMetadata(const QString &filePath, time_t modtime, qint64, const QByteArray &)
71 {
72 if (modtime <= 0) {
73 return {tr("Error updating metadata due to invalid modified time")};
74 }
75
76 FileSystem::setModTime(filePath, modtime);
77 return {};
78 }
79
createPlaceholder(const SyncFileItem & item)80 Result<void, QString> VfsXAttr::createPlaceholder(const SyncFileItem &item)
81 {
82 if (item._modtime <= 0) {
83 return {tr("Error updating metadata due to invalid modified time")};
84 }
85
86 const auto path = QString(_setupParams.filesystemPath + item._file);
87 QFile file(path);
88 if (file.exists() && file.size() > 1
89 && !FileSystem::verifyFileUnchanged(path, item._size, item._modtime)) {
90 return QStringLiteral("Cannot create a placeholder because a file with the placeholder name already exist");
91 }
92
93 if (!file.open(QFile::ReadWrite | QFile::Truncate)) {
94 return file.errorString();
95 }
96
97 file.write(" ");
98 file.close();
99 FileSystem::setModTime(path, item._modtime);
100 return xattr::addNextcloudPlaceholderAttributes(path);
101 }
102
dehydratePlaceholder(const SyncFileItem & item)103 Result<void, QString> VfsXAttr::dehydratePlaceholder(const SyncFileItem &item)
104 {
105 const auto path = QString(_setupParams.filesystemPath + item._file);
106 QFile file(path);
107 if (!file.remove()) {
108 return QStringLiteral("Couldn't remove the original file to dehydrate");
109 }
110 auto r = createPlaceholder(item);
111 if (!r) {
112 return r;
113 }
114
115 // Ensure the pin state isn't contradictory
116 const auto pin = pinState(item._file);
117 if (pin && *pin == PinState::AlwaysLocal) {
118 setPinState(item._renameTarget, PinState::Unspecified);
119 }
120 return {};
121 }
122
convertToPlaceholder(const QString &,const SyncFileItem &,const QString &)123 Result<Vfs::ConvertToPlaceholderResult, QString> VfsXAttr::convertToPlaceholder(const QString &, const SyncFileItem &, const QString &)
124 {
125 // Nothing necessary
126 return {ConvertToPlaceholderResult::Ok};
127 }
128
needsMetadataUpdate(const SyncFileItem &)129 bool VfsXAttr::needsMetadataUpdate(const SyncFileItem &)
130 {
131 return false;
132 }
133
isDehydratedPlaceholder(const QString & filePath)134 bool VfsXAttr::isDehydratedPlaceholder(const QString &filePath)
135 {
136 const auto fi = QFileInfo(filePath);
137 return fi.exists() &&
138 xattr::hasNextcloudPlaceholderAttributes(filePath);
139 }
140
statTypeVirtualFile(csync_file_stat_t * stat,void * statData)141 bool VfsXAttr::statTypeVirtualFile(csync_file_stat_t *stat, void *statData)
142 {
143 if (stat->type == ItemTypeDirectory) {
144 return false;
145 }
146
147 const auto parentPath = static_cast<QByteArray *>(statData);
148 Q_ASSERT(!parentPath->endsWith('/'));
149 Q_ASSERT(!stat->path.startsWith('/'));
150
151 const auto path = QByteArray(*parentPath + '/' + stat->path);
152 const auto pin = [=] {
153 const auto absolutePath = QString::fromUtf8(path);
154 Q_ASSERT(absolutePath.startsWith(params().filesystemPath.toUtf8()));
155 const auto folderPath = absolutePath.mid(params().filesystemPath.length());
156 return pinState(folderPath);
157 }();
158
159 if (xattr::hasNextcloudPlaceholderAttributes(path)) {
160 const auto shouldDownload = pin && (*pin == PinState::AlwaysLocal);
161 stat->type = shouldDownload ? ItemTypeVirtualFileDownload : ItemTypeVirtualFile;
162 return true;
163 } else {
164 const auto shouldDehydrate = pin && (*pin == PinState::OnlineOnly);
165 if (shouldDehydrate) {
166 stat->type = ItemTypeVirtualFileDehydration;
167 return true;
168 }
169 }
170 return false;
171 }
172
setPinState(const QString & folderPath,PinState state)173 bool VfsXAttr::setPinState(const QString &folderPath, PinState state)
174 {
175 return setPinStateInDb(folderPath, state);
176 }
177
pinState(const QString & folderPath)178 Optional<PinState> VfsXAttr::pinState(const QString &folderPath)
179 {
180 return pinStateInDb(folderPath);
181 }
182
availability(const QString & folderPath)183 Vfs::AvailabilityResult VfsXAttr::availability(const QString &folderPath)
184 {
185 return availabilityInDb(folderPath);
186 }
187
fileStatusChanged(const QString &,SyncFileStatus)188 void VfsXAttr::fileStatusChanged(const QString &, SyncFileStatus)
189 {
190 }
191
192 } // namespace OCC
193