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