1 /*
2  * Copyright (C) by Christian Kamm <mail@ckamm.de>
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_suffix.h"
16 
17 #include <QFile>
18 
19 #include "syncfileitem.h"
20 #include "filesystem.h"
21 #include "common/syncjournaldb.h"
22 
23 namespace OCC {
24 
VfsSuffix(QObject * parent)25 VfsSuffix::VfsSuffix(QObject *parent)
26     : Vfs(parent)
27 {
28 }
29 
~VfsSuffix()30 VfsSuffix::~VfsSuffix()
31 {
32 }
33 
mode() const34 Vfs::Mode VfsSuffix::mode() const
35 {
36     return WithSuffix;
37 }
38 
fileSuffix() const39 QString VfsSuffix::fileSuffix() const
40 {
41     return QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX);
42 }
43 
startImpl(const VfsSetupParams & params)44 void VfsSuffix::startImpl(const VfsSetupParams &params)
45 {
46     // It is unsafe for the database to contain any ".owncloud" file entries
47     // that are not marked as a virtual file. These could be real .owncloud
48     // files that were synced before vfs was enabled.
49     QByteArrayList toWipe;
50     params.journal->getFilesBelowPath("", [&toWipe](const SyncJournalFileRecord &rec) {
51         if (!rec.isVirtualFile() && rec._path.endsWith(APPLICATION_DOTVIRTUALFILE_SUFFIX))
52             toWipe.append(rec._path);
53     });
54     for (const auto &path : toWipe)
55         params.journal->deleteFileRecord(QString::fromUtf8(path));
56 }
57 
stop()58 void VfsSuffix::stop()
59 {
60 }
61 
unregisterFolder()62 void VfsSuffix::unregisterFolder()
63 {
64 }
65 
isHydrating() const66 bool VfsSuffix::isHydrating() const
67 {
68     return false;
69 }
70 
updateMetadata(const QString & filePath,time_t modtime,qint64,const QByteArray &)71 Result<void, QString> VfsSuffix::updateMetadata(const QString &filePath, time_t modtime, qint64, const QByteArray &)
72 {
73     FileSystem::setModTime(filePath, modtime);
74     return {};
75 }
76 
createPlaceholder(const SyncFileItem & item)77 Result<void, QString> VfsSuffix::createPlaceholder(const SyncFileItem &item)
78 {
79     // The concrete shape of the placeholder is also used in isDehydratedPlaceholder() below
80     QString fn = _setupParams.filesystemPath + item._file;
81     if (!fn.endsWith(fileSuffix())) {
82         OC_ASSERT_X(false, "vfs file isn't ending with suffix");
83         return QStringLiteral("vfs file isn't ending with suffix");
84     }
85 
86     QFile file(fn);
87     if (file.exists() && file.size() > 1
88         && !FileSystem::verifyFileUnchanged(fn, item._size, item._modtime)) {
89         return QStringLiteral("Cannot create a placeholder because a file with the placeholder name already exist");
90     }
91 
92     if (!file.open(QFile::ReadWrite | QFile::Truncate))
93         return file.errorString();
94 
95     file.write(" ");
96     file.close();
97     FileSystem::setModTime(fn, item._modtime);
98     return {};
99 }
100 
dehydratePlaceholder(const SyncFileItem & item)101 Result<void, QString> VfsSuffix::dehydratePlaceholder(const SyncFileItem &item)
102 {
103     SyncFileItem virtualItem(item);
104     virtualItem._file = item._renameTarget;
105     auto r = createPlaceholder(virtualItem);
106     if (!r)
107         return r;
108 
109     if (item._file != item._renameTarget) { // can be the same when renaming foo -> foo.owncloud to dehydrate
110         QFile::remove(_setupParams.filesystemPath + item._file);
111     }
112 
113     // Move the item's pin state
114     auto pin = _setupParams.journal->internalPinStates().rawForPath(item._file.toUtf8());
115     if (pin && *pin != PinState::Inherited) {
116         setPinState(item._renameTarget, *pin);
117         setPinState(item._file, PinState::Inherited);
118     }
119 
120     // Ensure the pin state isn't contradictory
121     pin = pinState(item._renameTarget);
122     if (pin && *pin == PinState::AlwaysLocal)
123         setPinState(item._renameTarget, PinState::Unspecified);
124     return {};
125 }
126 
convertToPlaceholder(const QString &,const SyncFileItem &,const QString &)127 Result<void, QString> VfsSuffix::convertToPlaceholder(const QString &, const SyncFileItem &, const QString &)
128 {
129     // Nothing necessary
130     return {};
131 }
132 
isDehydratedPlaceholder(const QString & filePath)133 bool VfsSuffix::isDehydratedPlaceholder(const QString &filePath)
134 {
135     if (!filePath.endsWith(fileSuffix()))
136         return false;
137     QFileInfo fi(filePath);
138     return fi.exists() && fi.size() == 1;
139 }
140 
statTypeVirtualFile(csync_file_stat_t * stat,void *)141 bool VfsSuffix::statTypeVirtualFile(csync_file_stat_t *stat, void *)
142 {
143     if (stat->path.endsWith(fileSuffix().toUtf8())) {
144         stat->type = ItemTypeVirtualFile;
145         return true;
146     }
147     return false;
148 }
149 
availability(const QString & folderPath)150 Vfs::AvailabilityResult VfsSuffix::availability(const QString &folderPath)
151 {
152     return availabilityInDb(folderPath);
153 }
154 
155 } // namespace OCC
156