aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/desktop-utils/lumina-fm/DirData.h
blob: 546dd6c5d3e2bb477abe99324df703cec588299b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//===========================================
//  Lumina-DE source code
//  Copyright (c) 2015, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================
//  This is the backend classes for fetching directory information/contents
//===========================================
#ifndef _LUMINA_FM_BACKGROUND_DATA_CLASSES_H
#define _LUMINA_FM_BACKGROUND_DATA_CLASSES_H

#include <QObject>
#include <QList>
#include <QString>
#include <QFileInfo>
#include  <QDir>

#include <LuminaXDG.h>
#include <LuminaUtils.h>

#define ZSNAPDIR QString("/.zfs/snapshot/")

#define DIR_DEBUG 0

//Class used for keeping track of directory information in the HASH
class LDirInfoList{
public:
	//Internal variables
	QDateTime lastcheck;
	QList<LFileInfo> list;
	QStringList fileNames; //list of filenames for comparison/checking sorting
	QString dirpath; //directory this structure was reading
	QString snapdir; //base snapshot directory (if one was requested/found)
	bool hashidden;
	QStringList mntpoints;

	//Access Functions
	LDirInfoList(QString path = ""){
	  dirpath = path;
	  list.clear();
	  fileNames.clear();
	  hashidden = false;
	  //Generate the list of all mountpoints if possible
	  if(LUtils::isValidBinary("zfs")){
	    mntpoints = LUtils::getCmdOutput("zfs list -H -o mountpoint").filter("/");
	    mntpoints.removeDuplicates();
	  }
	}
	~LDirInfoList(){}

	//(re)Load a directory contents
	void update(bool showhidden = false){
	  if(dirpath.isEmpty()){ return; } //nothing to do
	  //Assemble the structures
	  QDir dir(dirpath);
	  hashidden = showhidden;
	  if(!dir.exists()){
	    list.clear();
	    fileNames.clear();
	    dirpath.clear(); //invalid directory now
	    return;
	  }
	  if(dirpath.contains(ZSNAPDIR) && snapdir.isEmpty()){
	    snapdir = dirpath.section(ZSNAPDIR,0,0)+ZSNAPDIR; //no need to go looking for it later
	  }
	  QFileInfoList dirlist;
	  //Fill the structure
	  list.clear();
	  fileNames.clear();
	  lastcheck = QDateTime::currentDateTime().addMSecs(-500); //prevent missing any simultaneous dir changes
	  if(showhidden){ dirlist = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden , QDir::Name | QDir::DirsFirst); }
	  else{ dirlist = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot , QDir::Name | QDir::DirsFirst); }
	  //Simple add routine - can make it more dynamic/selective about updating individual items later
	    for(int i=0; i<dirlist.length(); i++){
	      list << LFileInfo(dirlist[i]); //generate the extra information for this file
	      fileNames << dirlist[i].fileName(); //add the filename to the list
	    }	
	}
	
	void findSnapDir(){
	  //Search the filesystem
	  if(dirpath.contains(ZSNAPDIR)){
	    snapdir = dirpath.section(ZSNAPDIR,0,0)+ZSNAPDIR; //no need to go looking for it
	  }else if(mntpoints.isEmpty()){ 
	    snapdir.clear(); //no zfs dirs available
	  }else{
	    //Only check the mountpoint associated with this directory
	    QString mnt;
	    for(int i=0; i<mntpoints.length(); i++){ 
	      if(dirpath == mntpoints[i]){ mnt = mntpoints[i]; break; }
	      else if(dirpath.startsWith(mntpoints[i]) && mntpoints[i].length()>mnt.length()){ mnt = mntpoints[i]; }
	    }
	    if(QFile::exists(mnt+ZSNAPDIR)){ snapdir = mnt+ZSNAPDIR; }
	    else{ snapdir.clear(); } //none found
	  }	  
	}

};

//This class is designed to be run in a background thread and get all the necessary info for a directory listing
class DirData : public QObject{
	Q_OBJECT
private:
	QHash<QString, LDirInfoList> HASH; //Where we cache any info for rapid access later

signals:
	void DirDataAvailable(QString, QString, LFileInfoList); //[ID, Dirpath, DATA]
	void SnapshotDataAvailable(QString, QString, QStringList); //[ID, BaseSnapDir, SnapNames]

public:
	//Variables
	bool showHidden; //Whether hidden files/dirs should be output
	bool zfsavailable; //Whether it should even bother looking for ZFS snapshots
	bool pauseData; //When paused - this will ignore any requests for information (need to manually refresh browsers after unpause)

	//Functions
	DirData(){ 
	  showHidden = false; 
	  zfsavailable = false;
	  pauseData = false;
	}
	~DirData(){}
	
public slots:
	void GetDirData(QString ID, QString dirpath){ 
          return;
	  if(pauseData){ return; }
	  if(DIR_DEBUG){ qDebug() << "GetDirData:" << ID << dirpath; }
	  //The ID is used when returning the info in a moment
	  //Make sure to use the canonical path in the HASH search - don't use 
	  QString canon = QFileInfo(dirpath).canonicalFilePath();
	  if(!HASH.contains(canon)){
	    //New directory (not previously loaded)
	    LDirInfoList info(canon);
	      info.update(showHidden);
	    HASH.insert(canon, info);
	  }else{
	    //See if the saved info needs to be updated
	    //if( (HASH.value(canon).hashidden != showHidden) || (QFileInfo(canon).lastModified() > HASH.value(canon).lastcheck) ){
	      HASH[canon].update(showHidden);
	    //}
	  }
	  if(DIR_DEBUG){ qDebug() << " -- Dir Data Found:" << ID << dirpath << HASH.value(canon).list.length(); }
	  emit DirDataAvailable(ID, dirpath, HASH.value(canon).list);
	}
	
	void GetSnapshotData(QString ID, QString dirpath){
	  if(pauseData){ return; }
	  if(DIR_DEBUG){ qDebug() << "GetSnapshotData:" << ID << dirpath; }
	  QString base; QStringList snaps;
	  //Only check if ZFS is flagged as available
	  if(zfsavailable){
	    //First find if the hash already has an entry for this directory
	    if(!HASH.contains(dirpath)){
	      LDirInfoList info(dirpath);
	      HASH.insert(dirpath,info);
	    }
	    //Now see if a snapshot directory has already been located
	    if(HASH.value(dirpath).snapdir.isEmpty()){
	      HASH[dirpath].findSnapDir();
	    }
	    //Now read off all the available snapshots
	    if(HASH.value(dirpath).snapdir != "-" && !HASH.value(dirpath).snapdir.isEmpty()){
	      //Good snapshot directory found - read off the current snapshots (can change regularly - don't cache this)
	      base = HASH.value(dirpath).snapdir;
	      QDir dir(base);
	      QString canon = QFileInfo(dirpath).canonicalFilePath();
	      QString dcanon = QString(dir.canonicalPath()+"/").section(ZSNAPDIR,0,0);
	      QString relpath = canon.section( dcanon+"/" ,-1);
	      //qDebug() << "Snapshot Dir:" << base << dcanon << "Dir:" << dirpath << canon << "Relpath:" << relpath;
	      snaps = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time |QDir::Reversed );
	      //Also remove any "empty" snapshots (might be leftover by tools like "zfsnap")
	      for(int i=0; i<snaps.length(); i++){
		dir.cd(base+"/"+snaps[i]);
	        if(relpath.isEmpty()){
		  //Make sure the snapshot is not empty
		  if(dir.count() < 3 && dir.exists() ){ snaps.removeAt(i); i--; } // "." and ".." are always in the count
		//Make sure the snapshot contains the directory we are looking for
	        }else if(!dir.exists(relpath)){ snaps.removeAt(i); i--; }
	      }
	      //NOTE: snaps are sorted oldest -> newest
	    }
	    
	  }
	  //if(DIR_DEBUG){ qDebug() << " -- Snap Data Found:" << ID << base << snaps; }
	  if(!base.isEmpty() && !snaps.isEmpty()){
	    emit SnapshotDataAvailable(ID, base, snaps); 
	  }
	}
	
};

#endif
bgstack15