2 #include <QDirIterator>
5 #include <QMimeDatabase>
87 #define VFS_HD_MAX_READABLE 104857600
123 connect(&
_watcher, SIGNAL(directoryChanged(
const QString &)),
this, SLOT(
modified(
const QString &)));
124 connect(&
_watcher, SIGNAL(fileChanged(
const QString &)),
this, SLOT(
modified(
const QString &)));
142 QDir d =
_info.dir();
143 if (!d.mkpath(d.path()))
145 printf(
"CAN'T CREATE PATH FOR WRITE: %s\n",qUtf8Printable(
_path));
152 if (file.open(QFile::WriteOnly))
154 file.write(
_raw?
"":
"{}");
181 _reason = QString(
"Entry does not exist: '%1'.").arg(
_path);
187 if (!
_info.isReadable())
189 _reason = QString(
"Permission denied: '%1'.").arg(
_path);
197 QFileInfoList list = dir.entryInfoList( QDir::AllEntries | QDir::Readable | QDir::NoDotAndDotDot );
202 for (
int i=0;i<list.length();i++)
214 o[info.fileName()] = info.isDir();
219 _size = o.keys().join(
"").length();
226 if(file.open(QFile::ReadOnly))
228 qint64 s = file.size();
237 QByteArray f = file.readAll();
245 d = QJsonDocument::fromJson(f,&e);
246 if (e.error != QJsonParseError::NoError)
249 VFS::ERROR( QString(
"offset: %1").arg(e.offset) );
334 _reason = QString(
"Illegal or unreadable path: %1").arg(
_path);
370 if (fi.lastModified() >
_info.lastModified())
371 VFS::WARN(
"File contents have changed since being cached. Changes will be overwritten. ("+
_path+
")");
375 if (wfile.open(QIODevice::WriteOnly))
385 qint64 w = wfile.write(D);
405 _reason = QString(
"Can't open '%1' for writing.").arg(
_path);
441 VFS_HD::VFS_HD(QString p, quint64 size,
bool create,
bool debug,
int flushInterval,
int expireInterval,
bool raw,
bool watch)
444 , _flushInterval(flushInterval)
445 , _expireInterval(expireInterval)
453 VFS::WARN( QString(
"VFS_HD: '%1' is a file... VFS requests will probably fail!").arg(
_path) );
458 VFS::WARN( QString(
"VFS_HD: '%1' does not exist... VFS requests will fail!").arg(
_path) );
462 VFS::WARN( QString(
"VFS_HD: '%1' does not exist... Creating it.").arg(
_path) );
463 if (!d.mkpath(
_path))
464 VFS::WARN( QString(
"VFS_HD: '%1' doesn't exist and could not be created... VFS requests will fail!").arg(
_path) );
498 return "Path: "+
_path+
"\nReal Path: "+i.canonicalFilePath()+
"\n"+s;
550 QMutexLocker _l(&
_lock);
561 if (r->
_metadata.value(
"closest").toBool(
false))
566 QStringList parents = r->
_path.split(
'/',Qt::SkipEmptyParts);
567 if (parents.length())
576 { r->
_reason =
"Can't find a closest entry to list... Already at the root of this node.";
583 { r->
_reason = QString(
"Invalid path: %1\n").arg(p);
589 QDirIterator::IteratorFlags f = QDirIterator::FollowSymlinks;
594 if (r->
_metadata.value(
"closest").toBool(
false))
599 QStringList parents = r->
_path.split(
'/',Qt::SkipEmptyParts);
600 if (parents.length())
609 { r->
_reason =
"Can't find a closest thing to list... Already at the root of this node.";
616 { r->
_reason =
"Can't LS a file, directories/containers only.";
624 if (r->
_metadata.value(
"closest").toBool(
false))
629 QDirIterator it(p,f);
630 bool entriesExcluded =
false;
631 bool ignorePermissions = r->
_metadata[
"ignorePermissions"].toBool(
false);
638 if (m!=
"." && m!=
"..")
640 if (ignorePermissions || i.isReadable())
646 entriesExcluded =
true;
655 if (r->
_metadata.value(
"sequence").toBool(
false))
659 r->
_data.setObject(a);
718 QMutexLocker l(&
_lock);
723 bool creating = r->
_metadata.value(
"createIfMissing").toBool(
false);
725 bool cache = r->
_metadata.value(
"cache").toBool(
true);
727 bool range = r->
_metadata.contains(
"range") ? true :
false;
734 VFS::WARN(
"Range request was present on non-raw VFS_HD. Range will be ignored.",0,
className());
782 if (r->
_metadata.value(
"sequence").toBool(
false) && en->
_info.isDir())
811 QMutexLocker l(&
_lock);
815 bool container = r->
_metadata.value(
"container").toBool(
false);
838 VFS::WARN( QString(
"About to create: '%1'").arg(p) );
843 QStringList dirs = r->
_path.split(
"/",Qt::SkipEmptyParts);
844 QString newFile = dirs.takeLast();
845 QString dirPath = dirs.join(
"/");
848 o[newFile] = container;
854 if (!
_cache.remove(dirPath))
898 QMutexLocker l(&
_lock);
935 r->
_reason = QString(
"Entry does not exist: '%1'.").arg(p);
940 if (!fi.isReadable())
942 r->
_reason = QString(
"Metadata permission denied: '%1'.").arg(p);
964 r->
_metadata[
"size"] = QString::number(fi.size());
965 r->
_metadata[
"mtime"] = QString::number(fi.lastModified().toSecsSinceEpoch());
969 r->
_metadata[
"size"] = QString::number(fi.size());
970 r->
_metadata[
"mtime"] = QString::number(fi.lastModified().toSecsSinceEpoch());
996 QMutexLocker l(&
_lock);
1000 bool creating = r->
_metadata.value(
"createIfMissing").toBool(
false);
1037 QJsonDocument fd = en->
getData();
1040 if (r->
_metadata.value(
"method").toString(
"json") ==
"delta")
1060 if (r->
_metadata.value(
"method").toString(
"json") ==
"delta")
1073 r->
_reason =
"Can't apply a json diff to _raw data.";
1122 s = QFile::remove(p);
1126 QStringList dirs = r->
_path.split(
"/",Qt::SkipEmptyParts);
1127 QString rmFile = dirs.takeLast();
1128 QString dirPath = dirs.join(
"/");
1132 QFileInfo info(rmFile);
1133 QJsonDocument diffData;
1135 o[ info.fileName() ] = QJsonValue::Null;
1136 diffData.setObject(o);
1140 r->
_data = diffData;
1148 r->
_reason = QString(
"Can't delete non-existent file and can't delete non-empty directory: '%1'").arg(p);
1177 if (r->
_metadata[
"createIfMissing"].toBool())
1189 r->
_reason = QString(
"Can't subscribe to non-existent entry: '%1'").arg(p);
1254 QJsonObject range = r->
_metadata[
"range"].toObject();
1259 if (range.contains(
"s"))
1260 start = (qint64) range[
"s"].toDouble();
1262 if (range.contains(
"e"))
1263 end = (qint64) range[
"e"].toDouble();
1268 QFileInfo info(
path);
1270 if (!info.exists() || info.isDir())
1272 r->
_reason =
"Can't fetch range on non-existent paths or directories: '"+
path+
"'";
1277 if (!info.isReadable())
1279 r->
_reason =
"Fetch range read permission denied: '"+
path+
"'";
1285 if (file.open(QFile::ReadOnly))
1287 qint64 size = file.size();
1289 if (start < 0 && -start <= size) start = size + start;
1290 if (end < 0 && -end <= size) end = size + end;
1291 if (end == 0) end = size - 1;
1295 r->
_reason = QString(
"Can't fetch range where start >= end (%1,%2)").arg(start).arg(end);
1302 r->
_reason = QString(
"Won't fetch range greater than %1 bytes. Requested %2 bytes.").arg(
VFS_HD_MAX_READABLE).arg(end-start+1);
1307 if (start >= size || end >= size)
1309 r->
_reason = QString(
"Can't fetch beyond the end of a file. Requested bytes %1-%2 on '%3'").arg(start).arg(end).arg(
path);
1314 qint64 bytes = end - start + 1;
1316 if (file.seek(start))
1318 QByteArray data = file.read(bytes);
1320 if (data.length() != bytes)
1322 r->
_reason = QString(
"Could not read %1 bytes from '%2'. Requested bytes %3-%4, but only %5 were returned.").arg(bytes).arg(
path).arg(start).arg(end).arg(data.length());
1336 r->
_reason = QString(
"Can't seek() to start value. Requested bytes %1-%2 on '%3'").arg(start).arg(end).arg(
path);
1343 r->
_reason = QString(
"Unable to open '%1'").arg(
path);
#define VFS_HD_MAX_READABLE
A subclass of VFS_datastore_cache_entry, used for disk access.
virtual void flush()
Actually write the file to disk.
QString _path
The actual file path.
QFileSystemWatcher _watcher
Watches for changes to the file or directory, and expires the entry on change.
QFileInfo _info
An info object, useful for type and metadata information.
virtual ~VFS_HD_cache_entry()
VFS_HD_cache_entry destructor.
VFS_HD_cache_entry(QString path, bool debug, int flushInterval, int expireInterval, bool create=false, bool container=false, bool raw=false, bool watch=false)
void modified(const QString &path)
A slot for when the _watcher detects that there has been a change.
virtual bool valid(QString &reason)
If the file or directory does not exist or is not readable, _valid is set to false.
const QString path()
Return the _path of this node.
bool _raw
Whether to default to JSON mode or RAW mode, which can be overridden with metadata.
virtual QByteArray icon()
The "disk" icon found in the VFS_icons library.
virtual void applyDiff(VFS_request *r)
Apply a diff to a file.
virtual QString reportDetails()
Report the current cache usage.
virtual void rm(VFS_request *r)
Attempt to delete a file.
virtual void read(VFS_request *r)
Read the contents of a directory or file.
virtual void fetchRange(VFS_request *r)
Fetch a byte range within a file.
Q_INVOKABLE VFS_HD(QString p, quint64 size=VFS_datastore_cache::DEFAULT_DATACACHE_SIZE, bool create=false, bool debug=false, int flushInterval=5000, int expireInterval=60000, bool raw=false, bool watch=false)
Create a VFS_HD node, with an optional create flag.
int _expireInterval
Interval for cache entries to expire, in milliseconds.
virtual void write(VFS_request *r)
Write data to a file.
static QMimeDatabase _mimeDatabase
The mime database, used for metadata.
bool _watch
Whether to set the watch flag on cache entries.
virtual void submit(VFS_request *r)
Submit data to a file, applying the data as a diff.
QString _path
The base prefix path for this VFS_HD node.
virtual void ls(VFS_request *r)
List the contents of a filesystem directory.
virtual void subscribe(VFS_request *r)
Subscribe to changes on a file or directory.
int _flushInterval
Interval for cache entries to flush, in milliseconds.
virtual void metadata(VFS_request *r)
Fetch the metadata for a file or directory.
A pure virtual class which must be subclassed for data storage.
quint64 _size
The size of the data. Always initialized to zero. Must be set in a subclass.
virtual bool valid(QString &reason)
virtual void setData(QJsonObject json)
Write data to the entry.
virtual QByteArray getRawData()
Return the data from an entry.
QString _reason
The reason this may be invalid. Set by a subclass.
bool _debug
Debug flag, for verbose output.
void expired(VFS_datastore_cache_entry *)
Signal emitted when a cache entry has expired and is no longer in use.
QByteArray _rawData
Raw binary data. Used if _raw=true.
virtual QJsonDocument getData()
Return the data from an entry.
bool _valid
Defaults to false. A subclass will determine if _valid is true, like for instance if an underlying fi...
bool _dirty
Changes have occurred but they are not commited to storage yet.
QJsonDocument _data
The actual cache data.
bool _raw
A convenience flag used in subclasses. Not used by this class.
bool insert(const QString &key, VFS_datastore_cache_entry *object)
Insert a new entry.
A base class for creating storage nodes in VFS.
VFS_datastore_cache _cache
The data cache.
static QString applyDeltaDiff(QString data, QJsonObject delta, QString trace="", QString user="server")
Apply a delta diff to a json object.
static QJsonObject applyJsonDiff(QJsonObject obj, QJsonObject diff, QString trace="", QString user="server")
Apply a json diff to a json object.
virtual QString reportDetails()
Report the current cache usage.
static char * get(QString which="")
Fetch an icon from the library.
static QString getType(QString _path, QString _default="unknownType")
Fetch the type of a file, based on path/filename.ext.
virtual VFS_request * createRequest(VFS_request::requestType type, QString path, QString user="unknown", QJsonDocument data=QJsonDocument(), QJsonObject metadata=QJsonObject(), bool dontDelete=false)
Create a new VFS_request with this object as _origin.
virtual void metadata(VFS_request *r)
Fetch the metadata of this node.
virtual void issueRequest(VFS_request *t)
A convenience function.
void diff(VFS_node *origin, VFS_request *t)
Emit a diff, which will trigger notifySubscribers() for a mounted node.
QString className()
Return the class name of a node.
virtual void unsubscribePath(QString path)
Unsubscribe all references to a subpath.
QMutex _lock
A recursive mutex that is local to this node.
virtual void rm(VFS_request *r)
Remove a child entry from a node, or the node itself.
virtual void subscribe(VFS_request *r)
Add an entry to this node's _subscription list.
The base class for all requests between nodes.
requestType
Requests perform one of these actions.
@ create
create a new file/path (2)
VFS_node * _origin
the origin of the request
QByteArray _rawData
the request payload, but raw data
requestType _requestType
the action this request is performing or requesting
QStringList _prefixPath
the prefix elements found while searching for the target
QString _user
who initiated this request, mostly for logging
QString _reason
if something (probably bad) happened, this is the reason
QString _path
the target path remnant... the remaining path element once the request has found its target
QString _originPath
the subpath of the origin node
bool _success
if the request was successfully completed
QJsonDocument _data
the request payload
virtual VFS_request * getCallback(VFS_node *receiver)
Create and chain a VFS_request for a receiver.
QJsonObject _metadata
the request payload
static void LOG(QString message, int level=0, QString user="server")
Send a message to the VFS::_messages VFS_stream.
static void ERROR(QString message, int level=0, QString user="server")
Send a message to the VFS::_errors VFS_stream.
static void CRITICAL(QString message)
Send a message to the VFS::_critical VFS_stream.
static void WARN(QString message, int level=0, QString user="server")
Send a message to the VFS::_warnings VFS_stream.
getter path
a getter DOCME
QJsonObject sequenceListing(QJsonObject l, QStringList types=sequenceTypes)
Given a list of filenames and a regex list, collapse the listing to sequences when possible.