1 module unde.file_manager.remove_paths;
2 
3 import unde.global_state;
4 import unde.lsblk;
5 import unde.lib;
6 import unde.scan;
7 import unde.path_mnt;
8 import unde.command_line.db;
9 
10 import std.stdio;
11 import std.conv;
12 import core.stdc.stdlib;
13 import std.math;
14 import berkeleydb.all;
15 import std.stdint;
16 import core.stdc.stdlib;
17 import std..string;
18 import std.algorithm.sorting;
19 import std.utf;
20 import std.concurrency;
21 import core.time;
22 import core.thread;
23 import std.datetime;
24 
25 import derelict.sdl2.sdl;
26 
27 import std.file;
28 
29 immutable DRect drect_zero = DRect(0, 0, 0, 0);
30 
31 private void
32 save_errors(FMGlobalState rgs, PathMnt path, Exception e)
33 {
34     Dbt key, data;
35     string path0 = path.get_key(rgs.lsblk);
36     key = path0;
37     auto res = rgs.db_map.get(rgs.txn, &key, &data);
38 
39     RectSize rectsize;
40     if (res == 0)
41         rectsize = data.to!(RectSize);
42 
43     ulong curr_time = Clock.currTime().toUnixTime();
44     rectsize.msg = to_char_array!80(strip_error(e.msg));
45     rectsize.msg_time = curr_time;
46     rectsize.msg_color = 0x80FF8080; // ARGB
47 
48     data = rectsize;
49     //writefln("WRITE - %s - %s", path0, rectsize);
50     res = rgs.db_map.put(rgs.txn, &key, &data);
51     if (res != 0)
52         throw new Exception("Path info to map-db not written");
53     rgs.OIT++;
54 
55     ssize_t first, last;
56     first = path0.indexOf("\0");
57     while (first != last)
58     {
59         last = path0.lastIndexOf("\0");
60         if (last > first)
61             path0 = path0[0..last];
62         else
63             path0 = path0[0..first+1];
64         key = path0;
65         res = rgs.db_map.get(rgs.txn, &key, &data);
66         if (res == 0)
67         {
68             rectsize = data.to!(RectSize);
69 
70             if (curr_time > rectsize.newest_msg_time)
71             {
72                 rectsize.newest_msg_time = curr_time;
73 
74                 data = rectsize;
75                 //writefln("WRITE - %s - %s", path0, rectsize);
76                 res = rgs.db_map.put(rgs.txn, &key, &data);
77                 if (res != 0)
78                     throw new Exception("Path info to map-db not written");
79                 rgs.OIT++;
80             }
81         }
82     }
83 }
84 
85 private void
86 remove_and_save_errors(FMGlobalState rgs, PathMnt path, bool dir=false)
87 {
88     try
89     {
90         if (dir)
91             rmdir(path);
92         else
93             remove(path);
94 
95 
96     }
97     catch (FileException e)
98     {
99         save_errors(rgs, path, e);
100     }
101 }
102 
103 package long
104 remove_path(FMGlobalState rgs, PathMnt path, bool root = true)
105 {
106     rgs.recommit();
107     //writefln("remove_path(%s)", path);
108     // exits on exits of parent
109     receiveTimeout( 0.seconds, 
110             (OwnerTerminated ot) {
111                 writefln("Abort removing due stopping parent");
112                 rgs.finish = true;
113             } );
114 
115     if (rgs.finish)
116         return 0;
117 
118     DirEntry de;
119     try
120     {
121         path.update(rgs.lsblk);
122         de = DirEntry(path);
123     }
124     catch (Exception e)
125     {
126         writefln("%s", e);
127         save_errors(rgs, path, e);
128         return 0;
129     }
130 
131     if (root)
132     {
133         string path0 = path.get_key(rgs.lsblk);
134         foreach (char c; "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
135         {
136             string m = "" ~ c;
137             Dbt key, data;
138             key = m;
139             auto res = rgs.db_marks.get(null, &key, &data);
140             if (res == 0)
141             {
142                 Mark mark = data.to!(Mark);
143                 string mpath = from_char_array(mark.path);
144                 if (mpath.startsWith(path0))
145                 {
146                     res = rgs.db_marks.del(null, &key);
147                     if (res != 0)
148                         throw new Exception("Mark info to marks-db not deleted");
149                 }
150             }
151         }
152     }
153 
154     if (de.isSymlink)
155     {
156         remove(path);
157         return 1;
158     }
159     else if (de.isDir)
160     {
161         string[] paths;
162         try
163         {
164             foreach (string name; dirEntries(path, SpanMode.shallow))
165             {
166                 paths ~= name;
167             }
168         }
169         catch (Exception e)
170         {
171             return 0;
172         }
173 
174         sort!("a < b")(paths);
175 
176         long files = 0;
177         foreach (string name; paths)
178         {
179             if (name != path)
180             {
181                 long f = remove_path(rgs, path.next(name), false);
182                 files += f;
183 
184             }
185         }
186 
187         remove_and_save_errors(rgs, path, true);
188 
189         if (exists(path))
190         {
191             Dbt key, data;
192             string path0 = path.get_key(rgs.lsblk);
193             key = path0;
194             //writefln("GET %s", info.uuid ~ subpath);
195             auto res = rgs.db_map.get(rgs.txn, &key, &data);
196             if (res == 0)
197             {
198                 RectSize rectsize = data.to!(RectSize);
199                 if ( rectsize.files >= files )
200                 {
201                     rectsize.files -= files;
202 
203                     data = rectsize;
204                     //writefln("WRITE - %s - %s", path0, rectsize);
205                     res = rgs.db_map.put(rgs.txn, &key, &data);
206                     if (res != 0)
207                         throw new Exception("Path info to map-db not written");
208                     rgs.OIT++;
209                 }
210             }
211         }
212         else
213         {
214             string path0 = path.get_key(rgs.lsblk);
215 
216             writefln("path0=%s", path0);
217             Dbc cursor2;
218             cursor2 = rgs.db_command_output.cursor(rgs.txn, 0);
219             scope(exit) cursor2.close();
220 
221             Dbt key2, data2;
222             string ks = get_key_for_command_out(command_out_key(path0, 0, 0));
223             key2 = ks;
224             auto res = cursor2.get(&key2, &data2, DB_SET_RANGE);
225             if (res == 0)
226             {
227                 do
228                 {
229                     string key_string = key2.to!(string);
230                     command_out_key cmd_out_key;
231                     parse_key_for_command_out(key_string, cmd_out_key);
232 
233                     writefln("OUTPUT %s - %s", cmd_out_key.cwd, path0);
234                     if (cmd_out_key.cwd == path0)
235                     {
236                         rgs.OIT++;
237                         if (rgs.is_time_to_recommit())
238                         {
239                             cursor2.close();
240                             rgs.recommit();
241                             cursor2 = rgs.db_command_output.cursor(rgs.txn, 0);
242                             res = cursor2.get(&key2, &data2, DB_SET_RANGE);
243                             if (res == DB_NOTFOUND)
244                             {
245                                 goto out1;
246                             }
247                         }
248 
249                         cursor2.del();
250                     }
251                     else
252                     {
253                         break;
254                     }
255 
256                 } while (cursor2.get(&key2, &data2, DB_NEXT) == 0);
257             }
258 out1:
259 
260             Dbc cursor3;
261             cursor3 = rgs.db_commands.cursor(rgs.txn, 0);
262             scope(exit) cursor3.close();
263 
264             Dbt key3, data3;
265             string ks3 = get_key_for_command(command_key(path0, 0));
266             key3 = ks3;
267             res = cursor3.get(&key3, &data3, DB_SET_RANGE);
268             if (res == 0)
269             {
270                 do
271                 {
272                     string key_string = key3.to!(string);
273                     command_key cmd_key;
274                     parse_key_for_command(key_string, cmd_key);
275 
276                     if (cmd_key.cwd == path0)
277                     {
278                         rgs.OIT++;
279                         if (rgs.is_time_to_recommit())
280                         {
281                             cursor3.close();
282                             rgs.recommit();
283                             cursor3 = rgs.db_commands.cursor(rgs.txn, 0);
284                             res = cursor3.get(&key3, &data3, DB_SET_RANGE);
285                             if (res == DB_NOTFOUND)
286                             {
287                                 goto out2;
288                             }
289                         }
290 
291                         cursor3.del();
292                     }
293                     else
294                     {
295                         break;
296                     }
297 
298                 } while (cursor3.get(&key3, &data3, DB_NEXT) == 0);
299             }
300 
301 out2:
302             Dbt key;
303             key = path0;
304             res = rgs.db_map.del(rgs.txn, &key);
305             if (res != 0)
306             {
307                 throw new Exception("Path info from map-db not removed");
308             }
309             rgs.OIT++;
310         }
311         return files;
312     }
313     else if (de.isFile)
314     {
315         remove_and_save_errors(rgs, path);
316         return 1;
317     }
318     else 
319     {
320         remove_and_save_errors(rgs, path);
321         return 1;
322     }
323 }
324 
325 private void
326 start_remove_paths(shared LsblkInfo[string] lsblk, immutable string[] paths, Tid tid)
327 {
328     writefln("Start removing %s", paths);
329 
330     try {
331         FMGlobalState rgs = new FMGlobalState();
332         scope(exit)
333         {
334             destroy(rgs);
335         }
336 
337         rgs.lsblk = to!(LsblkInfo[string])(lsblk);
338 
339         foreach(path; paths)
340         {
341             remove_path(rgs, PathMnt(rgs.lsblk, path));
342             rgs.commit();
343         }
344     } catch (shared(Throwable) exc) {
345         send(tid, exc);
346     }
347 
348     writefln("Finish removing %s", paths);
349     send(tid, thisTid);
350 }
351 
352 int remove_paths(GlobalState gs, immutable string[] paths)
353 {
354     foreach(tid, paths2; gs.removers)
355     {
356         if (paths2 == paths)
357         {
358             writefln("Remove Paths already in work");
359             return 0;
360         }
361     }
362 
363     shared LsblkInfo[string] lsblk = to!(shared LsblkInfo[string])(gs.lsblk);
364     auto tid = spawn(&start_remove_paths, lsblk, paths, thisTid);
365     gs.removers[tid] = paths.dup();
366     return 0;
367 }
368 
369 void check_removers(GlobalState gs)
370 {
371     // Look check_scanners
372 }
373