Remoto - VFS: VFS_hd.cpp Source File
Remoto - VFS
VFS_hd.cpp
Go to the documentation of this file.
1 
2 #include <QDirIterator>
3 #include <QFile>
4 #include <QDir>
5 #include <QMimeDatabase>
6 #include <QMimeType>
7 
8 #include "VFS.h"
9 #include "VFS_hd.h"
10 #include "VFS_base/VFS_icons.h"
11 #include "utilities/rutils.h"
12 
13 #include "VFS_base/VFS_node_type.h"
14 
15 
86 //#define VFS_HD_MAX_CACHEABLE 104857600 //100mb
87 #define VFS_HD_MAX_READABLE 104857600 //100mb
88 
89 //QFileSystemWatcher VFS_HD_cache_entry::_watcher();
90 QMimeDatabase VFS_HD::_mimeDatabase;
91 
112 VFS_HD_cache_entry::VFS_HD_cache_entry(QString path, bool debug, int flushInterval, int expireInterval, bool create, bool container, bool raw, bool watch)
113 : VFS_datastore_cache_entry(debug,flushInterval,expireInterval,raw)
114 , _path(path)
115 , _info(path)
116 , _watcher(this)
117 {
118  //_path = _info.canonicalFilePath();
119 
120  if (watch)
121  {
122  _watcher.addPath(path);
123  connect(&_watcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(modified(const QString &)));
124  connect(&_watcher, SIGNAL(fileChanged(const QString &)), this, SLOT(modified(const QString &)));
125  }
126 
127  if (_debug)
128  VFS::LOG( QString("Cache created: '%1'").arg(_path) );
129 
130  if (!_info.exists())
131  {
132  if (create)
133  {
134  //printf("creating: %s\n",qUtf8Printable(_path));
135 
136  // if (_info.exists())
137  // { VFS::ERROR( _reason = QString("Create would overwrite file '%1'").arg(_path) );
138  // _valid = false;
139  // }
140  // else
141  {
142  QDir d = _info.dir();
143  if (!d.mkpath(d.path()))
144  if (_debug)
145  printf("CAN'T CREATE PATH FOR WRITE: %s\n",qUtf8Printable(_path));
146 
147  //printf("creating: %s\n",qUtf8Printable(_path));
148 
149  if (!container)
150  {
151  QFile file(_path);
152  if (file.open(QFile::WriteOnly))
153  {
154  file.write(_raw?"":"{}"); //create an empty file or empty json file
155  _valid = true;
156  _size = 1; //FIXME: a size should be reported to the cache
157  file.close();
158  _dirty = true;
159  }
160  else
161  { VFS::ERROR( QString("Can't create file: %1").arg(_path) );
162  _valid = false;
163  }
164  }
165  else
166  {
167  if (d.mkdir(_path))
168  { _valid = true;
169  _size = 1;
170  _dirty = false;
171  }
172  else
173  VFS::ERROR( QString("Can't create directory: %1").arg(_path) );
174  }
175  }
176 
177  return;
178  }
179  else
180  {
181  _reason = QString("Entry does not exist: '%1'.").arg(_path);
182  _valid = false;
183  return;
184  }
185  }
186 
187  if (!_info.isReadable())
188  {
189  _reason = QString("Permission denied: '%1'.").arg(_path);
190  _valid = false;
191  return;
192  }
193 
194  if (_info.isDir())
195  {
196  QDir dir(_path);
197  QFileInfoList list = dir.entryInfoList( QDir::AllEntries | QDir::Readable | QDir::NoDotAndDotDot );
198 
199  QJsonObject o;
200  QFileInfo info;
201 
202  for (int i=0;i<list.length();i++)
203  {
204  info = list[i];
205 
206  /*
207  QJsonObject e;
208  e["name"] = info.baseName();
209  e["path"] = info.fileName();
210  e["container"] = info.isDir();
211 
212  o[info.fileName()] = e;
213  */
214  o[info.fileName()] = info.isDir();
215  }
216 
217  _valid = true;
218  //_size = qMax(1,list.length()); //FIXME: not sure where else to get size!
219  _size = o.keys().join("").length();
220  setData(o);
221  _dirty = false;
222  }
223  else
224  {
225  QFile file(_path);
226  if(file.open(QFile::ReadOnly))
227  {
228  qint64 s = file.size();
229 
230  if (s > VFS_HD_MAX_READABLE)
231  {
232  _reason = QString("Won't read files greater than %1 bytes. Requested file was %2 bytes.").arg(VFS_HD_MAX_READABLE).arg(s);
233  _valid = false;
234  return;
235  }
236 
237  QByteArray f = file.readAll();
238 
239  if (!_raw)
240  {
241  if (f.size())
242  {
243  QJsonDocument d;
244  QJsonParseError e;
245  d = QJsonDocument::fromJson(f,&e);
246  if (e.error != QJsonParseError::NoError)
247  {
248  VFS::ERROR( _reason = _path+" -- "+e.errorString() );
249  VFS::ERROR( QString("offset: %1").arg(e.offset) );
250  VFS::ERROR( QString(f) );
251  _valid = false;
252  return;
253  }
254 
255  setData(d.object());
256  _size = f.size();
257  }
258  }
259  else
260  {
261  // QJsonObject data;
262  // data["data"] = QString(f);
263  // d.setObject(data);
264 
265  setData(f);
266  }
267 
268  _valid = true;
269  _dirty = false;
270  }
271  else
272  {
273  //VFS::ERROR( _reason = QString("Unable to open '%1'.").arg(_path) );
274  _reason = QString("Unable to open '%1'.").arg(_path);
275  _valid = false;
276  }
277  }
278 }
279 
289 {
290  if (_debug)
291  VFS::LOG( QString("Cache deleted: '%1'").arg(_path) );
292 
293  if (_dirty && _valid)
294  flush();
295 }
296 
307 void VFS_HD_cache_entry::modified(const QString &path)
308 {
309  Q_UNUSED(path)
310 
311  //printf("MODIFIED: %s\n",qUtf8Printable(path));
312 
313  emit expired(this);
314 }
315 
326 bool VFS_HD_cache_entry::valid(QString &reason)
327 {
328  if (_valid)
329  return _valid;
330 
331  if (!_info.exists() || !_info.isReadable())
332  {
333  //VFS::ERROR( _reason = QString("Illegal or unreadable path: %1").arg(_path) );
334  _reason = QString("Illegal or unreadable path: %1").arg(_path);
335  _valid = false;
336  }
337 
338  return VFS_datastore_cache_entry::valid(reason);
339 }
340 
355 {
356  if (_debug)
357  VFS::LOG( QString("Flushing '%1'").arg(_path) );
358 
359  if (!_dirty)
360  return;
361 
362  if (_info.isDir())
363  { _dirty = false;
364  //_reason = "Can't flush data to a directory or container: "+_path;
365  return;
366  }
367 
368  QFileInfo fi(_path);
369 
370  if (fi.lastModified() > _info.lastModified())
371  VFS::WARN("File contents have changed since being cached. Changes will be overwritten. ("+_path+")");
372 
373  QFile wfile(_path);
374 
375  if (wfile.open(QIODevice::WriteOnly))
376  {
377  QByteArray D;
378 
379  if (_raw)
380  //D = _data.object()["data"].toString().toLocal8Bit();
381  D = _rawData;
382  else
383  D = _data.toJson();
384 
385  qint64 w = wfile.write(D);
386 
387  if (w != D.size()) //or == -1
388  {
389  //issue a VFS::CRITICAL message if disk is full or other deep failure happens
390  VFS::CRITICAL( _reason = QString("Unable to write to %1... Is the disk full or a permission problem?").arg(_path) );
391  _dirty = true;
392  }
393  else
394  { //_size = wfile.size();
395  //_size = w;
396  _dirty = false;
397  }
398 
399  wfile.close();
400 
401  _info.refresh(); //refresh the _info, like lastModified
402  }
403  else
404  {
405  _reason = QString("Can't open '%1' for writing.").arg(_path);
406  _dirty = true;
407  }
408 }
409 
410 /*
411 VFS_HD_cache::VFS_HD_cache(int size)
412 : VFS_datastore_cache(size)
413 {
414 }
415 
416 VFS_HD_cache::~VFS_HD_cache()
417 {
418 }
419 
420 VFS_datastore_cache_entry *VFS_HD_cache::createEntry(QString p)
421 {
422  return new VFS_HD_cache_entry(p);
423 }
424 */
425 
426 
441 VFS_HD::VFS_HD(QString p, quint64 size, bool create, bool debug, int flushInterval, int expireInterval, bool raw, bool watch)
442 : VFS_datastore(size, debug)
443 , _path(p)
444 , _flushInterval(flushInterval)
445 , _expireInterval(expireInterval)
446 , _raw(raw)
447 , _watch(watch)
448 {
449  QFileInfo i(_path);
450  //_path = i.canonicalFilePath(); //clean the path
451 
452  if (i.isFile())
453  VFS::WARN( QString("VFS_HD: '%1' is a file... VFS requests will probably fail!").arg(_path) );
454 
455  if (!i.isDir())
456  {
457  if (!create)
458  VFS::WARN( QString("VFS_HD: '%1' does not exist... VFS requests will fail!").arg(_path) );
459  else
460  {
461  QDir d = i.dir();
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) );
465  }
466  }
467 }
468 
470 {
471 }
472 
473 
481 QByteArray VFS_HD::icon()
482 {
483  return VFS_icons::get("disk");
484 }
485 
486 
493 {
494  QString s = VFS_datastore::reportDetails();
495 
496  QFileInfo i(_path);
497 
498  return "Path: "+_path+"\nReal Path: "+i.canonicalFilePath()+"\n"+s;
499 }
500 
501 
507 const QString VFS_HD::path()
508 {
509  return _path;
510 }
511 
512 
549 {
550  QMutexLocker _l(&_lock);
551 
552  //printf("VFS_HD::ls %s\n",qUtf8Printable(r->_initialPath));
553 
554  //QString p = _path+"/"+r->_path;
555  QString p = r->_path != "" ? _path+"/"+r->_path : _path;
556 
557  QFileInfo i(p);
558 
559  if (!i.exists())
560  {
561  if (r->_metadata.value("closest").toBool(false)) //if it is a call with 'closest' flag...
562  {
563  //VFS::ERROR("LS CLOSEST 1");
564  //printf("PATH: %s\nPREFIX: %s\n",qUtf8Printable(r->_path),qUtf8Printable(r->_prefixPath.join('/')));
565 
566  QStringList parents = r->_path.split('/',Qt::SkipEmptyParts);
567  if (parents.length())
568  {
569  parents.pop_back(); //climb up the path list to try to find something that does exist
570  VFS_request *s = r->getCallback(r->_origin);
571  s->_path = r->_prefixPath.join('/')+"/"+parents.join('/');
572  issueRequest(s);
573  return;
574  }
575  else
576  { r->_reason = "Can't find a closest entry to list... Already at the root of this node.";
577  r->_success = false;
578  //printf("METADATA:\n%s\n",qUtf8Printable( QJsonDocument::fromVariant(r->_metadata).toJson() ));
579  return;
580  }
581  }
582  else
583  { r->_reason = QString("Invalid path: %1\n").arg(p);
584  r->_success = false;
585  return;
586  }
587  }
588 
589  QDirIterator::IteratorFlags f = QDirIterator::FollowSymlinks;
590  //if (recursive) f |= QDirIterator::Subdirectories;
591 
592  if (i.isFile())
593  {
594  if (r->_metadata.value("closest").toBool(false)) //if it's a call with 'closest' flag, but it's a file...
595  {
596  //VFS::ERROR("LS CLOSEST 2");
597  //printf("PATH: %s\nPREFIX: %s\n",qUtf8Printable(r->_path),qUtf8Printable(r->_prefixPath.join('/')));
598 
599  QStringList parents = r->_path.split('/',Qt::SkipEmptyParts);
600  if (parents.length())
601  {
602  parents.pop_back(); //climb up a level to try to find a container
603  VFS_request *s = r->getCallback(r->_origin);
604  s->_path = r->_prefixPath.join('/')+"/"+parents.join('/');
605  issueRequest(s);
606  return;
607  }
608  else
609  { r->_reason = "Can't find a closest thing to list... Already at the root of this node.";
610  r->_success = false;
611  //printf("METADATA:\n%s\n",qUtf8Printable( QJsonDocument::fromVariant(r->_metadata).toJson() ));
612  return;
613  }
614  }
615  else
616  { r->_reason = "Can't LS a file, directories/containers only.";
617  r->_success = false;
618  //printf("METADATA:\n%s\n",qUtf8Printable( QJsonDocument::fromVariant(r->_metadata).toJson() ));
619  return;
620  }
621  }
622 
623  //FIXME: arguably, this should be in remotoserver, or a more global place
624  if (r->_metadata.value("closest").toBool(false))
625  r->_metadata["closestContainer"] = r->_prefixPath.join('/') + "/" + r->_path;
626 
627  QString n,m;
628  QJsonObject a;
629  QDirIterator it(p,f);
630  bool entriesExcluded = false;
631  bool ignorePermissions = r->_metadata["ignorePermissions"].toBool(false);
632 
633  while (it.hasNext())
634  {
635  n = it.next();
636  m = it.fileName();
637  i = it.fileInfo();
638  if (m!="." && m!="..")
639  {
640  if (ignorePermissions || i.isReadable()) //check permission
641  {
642  //a.push_back( m+(i.isDir()?"/":"") );
643  a[m] = i.isDir();
644  }
645  else
646  entriesExcluded = true;
647  }
648  }
649 
650  if (entriesExcluded)
651  r->_metadata["entriesExcluded"] = true;
652 
653  //printf("HD ls metadata: %s\n",qUtf8Printable( QJsonDocument(r->_metadata).toJson() ));
654 
655  if (r->_metadata.value("sequence").toBool(false))
657 
658  //r->_data = QJsonDocument(a);
659  r->_data.setObject(a);
660  r->_success = true;
661 }
662 
663 /*
664 VFS_node *VFS_HD::find(VFS_request *r)
665 {
666  QMutexLocker l(&_lock);
667 
668  QString p = _path+"/"+r->_path;
669  QFileInfo i(p);
670  if (i.exists())
671  {
672  return this;
673  }
674 
675  //if (allowNonExisting && path.length())
676  //{
677  // QString newFile = path.takeLast(); //only allow one entry to be created.... not a whole path of directories
678  // QString sub = path.join("/");
679  // QString p = _path+"/"+sub;
680  // QFileInfo i(p);
681  // if (i.exists())
682  // {
683  // //VFS::WARN( QString("Returning path prefix for non-existent find request: '%1'").arg(p) );
684  // return this;
685  // }
686  //}
687 
688  VFS::ERROR( QString("%1 Illegal path: '%2'").arg(className()).arg(p) );
689 
690  return 0;
691 }
692 */
693 
717 {
718  QMutexLocker l(&_lock);
719 
720  //QString p = _path+"/"+r->_path;
721  QString p = r->_path != "" ? _path+"/"+r->_path : _path;
722 
723  bool creating = r->_metadata.value("createIfMissing").toBool(false);
724  //bool container = r->_metadata.value("container",false).toBool();
725  bool cache = r->_metadata.value("cache").toBool(true);
726  bool raw = r->_metadata.value("raw").toBool(false) || _raw;
727  bool range = r->_metadata.contains("range") ? true : false;
728 
729  if (range)
730  {
731  if (raw)
732  return fetchRange(r);
733  else
734  VFS::WARN("Range request was present on non-raw VFS_HD. Range will be ignored.",0,className());
735  }
736 
737  //printf("VFS_HD::read RAW:%d PATH:'%s'\n",raw,qUtf8Printable(p));
738 
739  VFS_HD_cache_entry *en = static_cast<VFS_HD_cache_entry *>( _cache[r->_path] );
740  //VFS_datastore_cache_entry *en = _cache[r->_path];
741  if (!en)
742  {
743  //FIXME: check if _raw != raw ... it's possible that one call is for raw and another is for non-raw, but the cache won't be able to tell the difference
744 
745  en = new VFS_HD_cache_entry(p,_debug,_flushInterval,_expireInterval,false,false,raw,_watch);
746  //printf("HD DATASTORE READ::CREATE: %p '%s'\n",this,qUtf8Printable(r->_path));
747 
748  if (!en->valid(r->_reason))
749  {
750  delete en;
751 
752  if (creating)
753  {
754  //printf("CREATE FROM READ: %s\n%s\n",qUtf8Printable(r->_path),qUtf8Printable(r->_data.toJson()));
755 
758  write(r);
759  r->_requestType = t;
760  return;
761  }
762 
763  r->_success = false;
764  return;
765  }
766 
767  if (cache)
768  if (!_cache.insert(r->_path,en))
769  { //printf( "%s\n", qUtf8Printable(QString("%1::read(): '%2' was too large to cache").arg(className()).arg(r->_path)) );
770  VFS::WARN( QString("%1::read(): '%2' was too large to cache").arg(className()).arg(r->_path), 9 );
771  }
772  }
773 
774  //printf("HD read metadata: %s\n",qUtf8Printable( QJsonDocument(r->_metadata).toJson() ));
775 
776  if (raw && !en->getRawData().isEmpty())
777  r->_rawData = en->getRawData();
778  else
779  {
780  r->_data = en->getData();
781 
782  if (r->_metadata.value("sequence").toBool(false) && en->_info.isDir())
783  r->_data.setObject( rutils::sequenceListing(r->_data.object()) );
784  }
785 
786  if (!_cache.contains(r->_path))
787  delete en;
788 
789  r->_success = true;
790 }
791 
810 {
811  QMutexLocker l(&_lock);
812 
813  QString p = r->_path != "" ? _path+"/"+r->_path : _path;
814  bool creating = r->_requestType == VFS_request::create;
815  bool container = r->_metadata.value("container").toBool(false);
816  //bool raw = r->_metadata.value("raw").toBool(false) || _raw;
817 
819  if (!en)
820  {
821  en = new VFS_HD_cache_entry(p,_debug,_flushInterval,_expireInterval,creating,container,_raw,_watch);
822  //en->setData(r->_data.object());
823  //printf("DATA:\n%s\n",qUtf8Printable(r->_data.toJson()));
824 
825  if (!en->valid(r->_reason))
826  { r->_success = false;
827  delete en;
828  //printf("INVALID DATASTORE ENTRY!\n");
829  //printf("REASON: %s\n",qUtf8Printable(r->_reason));
830  return;
831  }
832 
833  r->_success = true;
834 
835  if (creating)
836  {
837  if (_debug)
838  VFS::WARN( QString("About to create: '%1'").arg(p) );
839 
840  //Create a diff for anything that is subscribed to the containing directory
841  //If container==true, the VFS_HD_cache_entry will create a directory.
842 
843  QStringList dirs = r->_path.split("/",Qt::SkipEmptyParts);
844  QString newFile = dirs.takeLast();
845  QString dirPath = dirs.join("/");
846 
847  QJsonObject o;
848  o[newFile] = container;
849  //FIXME: may need to write metadata here
850  VFS_request *c = createRequest(r->_requestType,dirPath,r->_user,QJsonDocument(o));
851  emit diff(this,c);
852  delete c;
853 
854  if (!_cache.remove(dirPath)) //it will be re-created if needed
855  { //printf("Unable to remove cache entry: %s\n",qUtf8Printable(dirPath));
856  }
857  }
858 
859  //if (!_cache.insert(r->_path,en))
860  // VFS::WARN( QString("%1::write(): '%2' was too large to cache").arg(className()).arg(r->_path) );
861  }
862 
863  if (!_raw)
864  en->setData(r->_data.object());
865  else
866  en->setData(r->_rawData);
867 
868  //take() the cache entry if it exists
869  if (_cache.contains(r->_path))
870  _cache.take(r->_path);
871 
872  //re-insert the cache entry with the new size
873  if (!_cache.insert(r->_path,en))
874  VFS::WARN( QString("%1::write(): '%2' was too large to cache").arg(className()).arg(r->_path), 9 );
875 
876  //delete the cache entry if it didn't make it into the cache
877  if (!_cache.contains(r->_path))
878  delete en;
879 
880  r->_success = true;
881 
882  emit diff(this,r);
883 }
884 
897 {
898  QMutexLocker l(&_lock);
899 
900  /*
901  if (r->_path == "")
902  {
903  //FIXME: the VFS_HD node itself may have a root metadata component
904  }
905  else
906  {
907 
908  }
909  */
910 
911  //QString p = _path+"/"+r->_path;
912  QString p = r->_path != "" ? _path+"/"+r->_path : _path;
913 
914  //NOTE: should not do this, because it may be a raw read or need creation flags
915  /*
916  VFS_datastore_cache_entry *en = _cache[r->_path];
917  if (!en)
918  {
919  en = new VFS_HD_cache_entry(p,_debug,_flushInterval,_expireInterval);
920 
921  if (!en->valid(r->_reason))
922  { r->_success = false;
923  delete en;
924  return;
925  }
926 
927  if (!_cache.insert(r->_path,en))
928  VFS::WARN( QString("%1::metadata(): '%2' was too large to cache").arg(className()).arg(r->_path), 9 );
929  }
930  */
931 
932  QFileInfo fi(p);
933  if (!fi.exists())
934  {
935  r->_reason = QString("Entry does not exist: '%1'.").arg(p);
936  r->_success = false;
937  return;
938  }
939 
940  if (!fi.isReadable())
941  {
942  r->_reason = QString("Metadata permission denied: '%1'.").arg(p);
943  r->_success = false;
944  return;
945  }
946 
947  if (r->_path == "")
948  return VFS_datastore::metadata(r);
949  else
950  r->_metadata["icon"] = fi.isDir() ? VFS_icons::get("folder") : VFS_icons::get("document");
951 
952  if (_raw)
953  r->_metadata["raw"] = true;
954 
955  QString t = VFS_node_type::getType(p,"");
956 
957  if (t == "")
958  {
959  QMimeType mime = _mimeDatabase.mimeTypeForFile(fi);
960  t = mime.name();
961 
962  r->_metadata["mime"] = t;
963  r->_metadata["type"] = mime.comment();
964  r->_metadata["size"] = QString::number(fi.size());
965  r->_metadata["mtime"] = QString::number(fi.lastModified().toSecsSinceEpoch());
966  }
967  else
968  { r->_metadata["type"] = t;
969  r->_metadata["size"] = QString::number(fi.size());
970  r->_metadata["mtime"] = QString::number(fi.lastModified().toSecsSinceEpoch());
971  }
972 
973  r->_success = true;
974 }
975 
995 {
996  QMutexLocker l(&_lock);
997 
998  //QString p = _path+"/"+r->_path;
999  QString p = r->_path != "" ? _path+"/"+r->_path : _path;
1000  bool creating = r->_metadata.value("createIfMissing").toBool(false);
1001  bool raw = r->_metadata.value("raw").toBool(false) || _raw;
1002 
1003  //FIXME: need to cover the case of attempting to submit to a directory, which should be illegal.
1004 
1006  if (!en)
1007  {
1009 
1010  if (!en->valid(r->_reason))
1011  {
1012  delete en;
1013 
1014  if (creating)
1015  {
1016  //printf("CREATE FROM SUBMIT: %s\n%s\n",qUtf8Printable(r->_path),qUtf8Printable(r->_data.toJson()));
1017 
1020  write(r);
1021  r->_requestType = t;
1022  return;
1023  }
1024  else
1025  {
1026  r->_success = false;
1027  return;
1028  }
1029  }
1030 
1031  if (!_cache.insert(r->_path,en))
1032  VFS::WARN( QString("%1::submit(): '%2' was too large to cache").arg(className()).arg(r->_path), 9 );
1033  }
1034 
1035  if (!raw)
1036  {
1037  QJsonDocument fd = en->getData();
1038  QJsonObject o;
1039 
1040  if (r->_metadata.value("method").toString("json") == "delta")
1041  {
1042  //o = applyDeltaDiff( fd.object(), r->_data.object(), "", r->_user );
1043  QString s = applyDeltaDiff( fd.object()["data"].toString(), r->_data.object(), "", r->_user );
1044  o["data"] = s;
1045  }
1046  else
1047  o = applyJsonDiff( fd.object(), r->_data.object(), "", r->_user );
1048 
1049  en->setData(o);
1050 
1051  r->_success = true;
1052 
1053  emit diff(this,r);
1054  }
1055  else
1056  {
1057  QByteArray fd = en->getRawData();
1058  QJsonObject o;
1059 
1060  if (r->_metadata.value("method").toString("json") == "delta")
1061  {
1062  QString s = applyDeltaDiff( QString::fromUtf8(fd), r->_data.object(), "", r->_user );
1063 
1064  en->setData( s.toUtf8() );
1065 
1066  r->_success = true;
1067 
1068  emit diff(this,r);
1069  }
1070  else
1071  { //o = applyJsonDiff( fd.object(), r->_data.object(), "", r->_user );
1072  r->_success = false;
1073  r->_reason = "Can't apply a json diff to _raw data.";
1074  }
1075  }
1076 
1077  if (!_cache.contains(r->_path))
1078  delete en;
1079 }
1080 
1100 {
1101  //check if path is empty... that means that this node is to be removed
1102  if (r->_path=="")
1103  {
1104  VFS_node::rm(r);
1105  return;
1106  }
1107 
1108  //QString p = _path+"/"+r->_path;
1109  QString p = r->_path != "" ? _path+"/"+r->_path : _path;
1110 
1111  QFileInfo fi(p);
1112  bool s = false;
1113 
1114  _cache.remove(r->_path);
1115 
1116  if (fi.isDir())
1117  {
1118  QDir d(p);
1119  s = d.rmdir(p);
1120  }
1121  else
1122  s = QFile::remove(p);
1123 
1124  if (s)
1125  {
1126  QStringList dirs = r->_path.split("/",Qt::SkipEmptyParts);
1127  QString rmFile = dirs.takeLast();
1128  QString dirPath = dirs.join("/");
1129 
1130  _cache.remove(dirPath); //it will be re-created if needed.
1131 
1132  QFileInfo info(rmFile);
1133  QJsonDocument diffData;
1134  QJsonObject o;
1135  o[ info.fileName() ] = QJsonValue::Null; //null means remove
1136  diffData.setObject(o);
1137 
1138  unsubscribePath(r->_path); //unsubscribePath on this path... it no longer exists!
1139 
1140  r->_data = diffData;
1141  r->_path = dirPath;
1142  r->_success = true;
1143 
1144  emit diff(this,r);
1145  }
1146  else
1147  {
1148  r->_reason = QString("Can't delete non-existent file and can't delete non-empty directory: '%1'").arg(p);
1149  r->_success = false;
1150  }
1151 }
1152 
1153 
1169 {
1170  //QString p = _path+"/"+r->_path;
1171  QString p = r->_path != "" ? _path+"/"+r->_path : _path;
1172 
1173  QFileInfo i(p);
1174 
1175  if (!i.exists())
1176  {
1177  if (r->_metadata["createIfMissing"].toBool())
1178  {
1181  write(r);
1182  if (r->_success)
1183  { r->_requestType = t;
1185  }
1186  }
1187  else
1188  {
1189  r->_reason = QString("Can't subscribe to non-existent entry: '%1'").arg(p);
1190  r->_success = false;
1191  }
1192  }
1193  else
1195 }
1196 
1197 
1207 //FIXME: this needs a deeper look...
1208 //it seems odd that this node and not others would need to use originPath
1210 {
1211  //QString p = r->_path;
1212  //r->_initialPath = r->_prefixPath.join('/')+"/"+r->_path;
1213  r->_path = r->_originPath;
1214 
1215  //printf("VFS_HD diff: %s %s\n",qUtf8Printable(r->_path),qUtf8Printable(r->_initialPath));
1216 
1217  submit(r);
1218 
1219  //r->_path = p;
1220 
1221  //VFS_node::applyDiff(r);
1222 }
1223 
1253 {
1254  QJsonObject range = r->_metadata["range"].toObject();
1255 
1256  qint64 start(0);
1257  qint64 end(0);
1258 
1259  if (range.contains("s"))
1260  start = (qint64) range["s"].toDouble();
1261 
1262  if (range.contains("e"))
1263  end = (qint64) range["e"].toDouble();
1264 
1265  //printf("RANGE: %lld %lld\n",start,end);
1266 
1267  QString path = (r->_path != "") ? _path+"/"+r->_path : _path;
1268  QFileInfo info(path);
1269 
1270  if (!info.exists() || info.isDir())
1271  {
1272  r->_reason = "Can't fetch range on non-existent paths or directories: '"+path+"'";
1273  r->_success = false;
1274  return;
1275  }
1276 
1277  if (!info.isReadable())
1278  {
1279  r->_reason = "Fetch range read permission denied: '"+path+"'";
1280  r->_success = false;
1281  return;
1282  }
1283 
1284  QFile file(path);
1285  if (file.open(QFile::ReadOnly))
1286  {
1287  qint64 size = file.size();
1288 
1289  if (start < 0 && -start <= size) start = size + start; //which should now be positive or zero
1290  if (end < 0 && -end <= size) end = size + end; //which should now be positive or zero
1291  if (end == 0) end = size - 1; //the end of the file, which must be offset by one
1292 
1293  if (start > end)
1294  {
1295  r->_reason = QString("Can't fetch range where start >= end (%1,%2)").arg(start).arg(end);
1296  r->_success = false;
1297  return;
1298  }
1299 
1300  if (end - start > VFS_HD_MAX_READABLE)
1301  {
1302  r->_reason = QString("Won't fetch range greater than %1 bytes. Requested %2 bytes.").arg(VFS_HD_MAX_READABLE).arg(end-start+1);
1303  r->_success = false;
1304  return;
1305  }
1306 
1307  if (start >= size || end >= size)
1308  {
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);
1310  r->_success = false;
1311  return;
1312  }
1313 
1314  qint64 bytes = end - start + 1;
1315 
1316  if (file.seek(start))
1317  {
1318  QByteArray data = file.read(bytes);
1319 
1320  if (data.length() != bytes)
1321  {
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());
1323  r->_success = false;
1324  return;
1325  }
1326 
1327  range["s"] = start;
1328  range["e"] = end;
1329  r->_metadata["range"] = range;
1330 
1331  r->_rawData = data;
1332  r->_success = true;
1333  }
1334  else
1335  {
1336  r->_reason = QString("Can't seek() to start value. Requested bytes %1-%2 on '%3'").arg(start).arg(end).arg(path);
1337  r->_success = false;
1338  return;
1339  }
1340  }
1341  else
1342  {
1343  r->_reason = QString("Unable to open '%1'").arg(path);
1344  r->_success = false;
1345  }
1346 }
#define VFS_HD_MAX_READABLE
Definition: VFS_hd.cpp:87
A subclass of VFS_datastore_cache_entry, used for disk access.
Definition: VFS_hd.h:15
virtual void flush()
Actually write the file to disk.
Definition: VFS_hd.cpp:354
QString _path
The actual file path.
Definition: VFS_hd.h:27
QFileSystemWatcher _watcher
Watches for changes to the file or directory, and expires the entry on change.
Definition: VFS_hd.h:29
QFileInfo _info
An info object, useful for type and metadata information.
Definition: VFS_hd.h:28
virtual ~VFS_HD_cache_entry()
VFS_HD_cache_entry destructor.
Definition: VFS_hd.cpp:288
VFS_HD_cache_entry(QString path, bool debug, int flushInterval, int expireInterval, bool create=false, bool container=false, bool raw=false, bool watch=false)
Definition: VFS_hd.cpp:112
void modified(const QString &path)
A slot for when the _watcher detects that there has been a change.
Definition: VFS_hd.cpp:307
virtual bool valid(QString &reason)
If the file or directory does not exist or is not readable, _valid is set to false.
Definition: VFS_hd.cpp:326
const QString path()
Return the _path of this node.
Definition: VFS_hd.cpp:507
bool _raw
Whether to default to JSON mode or RAW mode, which can be overridden with metadata.
Definition: VFS_hd.h:70
virtual QByteArray icon()
The "disk" icon found in the VFS_icons library.
Definition: VFS_hd.cpp:481
virtual void applyDiff(VFS_request *r)
Apply a diff to a file.
Definition: VFS_hd.cpp:1209
virtual QString reportDetails()
Report the current cache usage.
Definition: VFS_hd.cpp:492
virtual void rm(VFS_request *r)
Attempt to delete a file.
Definition: VFS_hd.cpp:1099
virtual void read(VFS_request *r)
Read the contents of a directory or file.
Definition: VFS_hd.cpp:716
virtual void fetchRange(VFS_request *r)
Fetch a byte range within a file.
Definition: VFS_hd.cpp:1252
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.
Definition: VFS_hd.cpp:441
int _expireInterval
Interval for cache entries to expire, in milliseconds.
Definition: VFS_hd.h:69
virtual void write(VFS_request *r)
Write data to a file.
Definition: VFS_hd.cpp:809
static QMimeDatabase _mimeDatabase
The mime database, used for metadata.
Definition: VFS_hd.h:73
bool _watch
Whether to set the watch flag on cache entries.
Definition: VFS_hd.h:71
virtual void submit(VFS_request *r)
Submit data to a file, applying the data as a diff.
Definition: VFS_hd.cpp:994
QString _path
The base prefix path for this VFS_HD node.
Definition: VFS_hd.h:67
virtual void ls(VFS_request *r)
List the contents of a filesystem directory.
Definition: VFS_hd.cpp:548
virtual void subscribe(VFS_request *r)
Subscribe to changes on a file or directory.
Definition: VFS_hd.cpp:1168
int _flushInterval
Interval for cache entries to flush, in milliseconds.
Definition: VFS_hd.h:68
virtual void metadata(VFS_request *r)
Fetch the metadata for a file or directory.
Definition: VFS_hd.cpp:896
virtual ~VFS_HD()
Definition: VFS_hd.cpp:469
A pure virtual class which must be subclassed for data storage.
Definition: VFS_datastore.h:13
quint64 _size
The size of the data. Always initialized to zero. Must be set in a subclass.
Definition: VFS_datastore.h:34
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.
Definition: VFS_datastore.h:38
bool _debug
Debug flag, for verbose output.
Definition: VFS_datastore.h:39
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.
Definition: VFS_datastore.h:33
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...
Definition: VFS_datastore.h:37
bool _dirty
Changes have occurred but they are not commited to storage yet.
Definition: VFS_datastore.h:36
QJsonDocument _data
The actual cache data.
Definition: VFS_datastore.h:32
bool _raw
A convenience flag used in subclasses. Not used by this class.
Definition: VFS_datastore.h:35
bool insert(const QString &key, VFS_datastore_cache_entry *object)
Insert a new entry.
A base class for creating storage nodes in VFS.
Definition: VFS_datastore.h:84
VFS_datastore_cache _cache
The data cache.
bool _debug
Debug mode.
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.
Definition: VFS_icons.cpp:34
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.
Definition: VFS_node.cpp:1913
virtual void metadata(VFS_request *r)
Fetch the metadata of this node.
Definition: VFS_node.cpp:797
virtual void issueRequest(VFS_request *t)
A convenience function.
Definition: VFS_node.cpp:1933
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.
Definition: VFS_node.cpp:2039
virtual void unsubscribePath(QString path)
Unsubscribe all references to a subpath.
Definition: VFS_node.cpp:1384
QMutex _lock
A recursive mutex that is local to this node.
Definition: VFS_node.h:178
virtual void rm(VFS_request *r)
Remove a child entry from a node, or the node itself.
Definition: VFS_node.cpp:1005
virtual void subscribe(VFS_request *r)
Add an entry to this node's _subscription list.
Definition: VFS_node.cpp:1204
The base class for all requests between nodes.
Definition: VFS_node.h:54
requestType
Requests perform one of these actions.
Definition: VFS_node.h:63
@ create
create a new file/path (2)
Definition: VFS_node.h:66
VFS_node * _origin
the origin of the request
Definition: VFS_node.h:89
QByteArray _rawData
the request payload, but raw data
Definition: VFS_node.h:103
requestType _requestType
the action this request is performing or requesting
Definition: VFS_node.h:87
QStringList _prefixPath
the prefix elements found while searching for the target
Definition: VFS_node.h:94
QString _user
who initiated this request, mostly for logging
Definition: VFS_node.h:106
QString _reason
if something (probably bad) happened, this is the reason
Definition: VFS_node.h:108
QString _path
the target path remnant... the remaining path element once the request has found its target
Definition: VFS_node.h:95
QString _originPath
the subpath of the origin node
Definition: VFS_node.h:90
bool _success
if the request was successfully completed
Definition: VFS_node.h:107
QJsonDocument _data
the request payload
Definition: VFS_node.h:102
virtual VFS_request * getCallback(VFS_node *receiver)
Create and chain a VFS_request for a receiver.
Definition: VFS_node.cpp:316
QJsonObject _metadata
the request payload
Definition: VFS_node.h:101
static void LOG(QString message, int level=0, QString user="server")
Send a message to the VFS::_messages VFS_stream.
Definition: VFS.cpp:209
static void ERROR(QString message, int level=0, QString user="server")
Send a message to the VFS::_errors VFS_stream.
Definition: VFS.cpp:307
static void CRITICAL(QString message)
Send a message to the VFS::_critical VFS_stream.
Definition: VFS.cpp:357
static void WARN(QString message, int level=0, QString user="server")
Send a message to the VFS::_warnings VFS_stream.
Definition: VFS.cpp:258
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.
Definition: rutils.cpp:183