3
* TeeJee.FileSystem.vala
5
* Copyright 2016 Tony George <teejee2008@gmail.com>
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25
namespace TeeJee.FileSystem{
27
/* Convenience functions for handling files and directories */
30
using TeeJee.ProcessHelper;
34
public const int64 KB = 1000;
35
public const int64 MB = 1000 * KB;
36
public const int64 GB = 1000 * MB;
37
public const int64 TB = 1000 * GB;
38
public const int64 KiB = 1024;
39
public const int64 MiB = 1024 * KiB;
40
public const int64 GiB = 1024 * MiB;
41
public const int64 TiB = 1024 * GiB;
43
// path helpers ----------------------------
45
public string file_parent(string file_path){
46
return File.new_for_path(file_path).get_parent().get_path();
49
public string file_basename(string file_path){
50
return File.new_for_path(file_path).get_basename();
53
public string path_combine(string path1, string path2){
54
return GLib.Path.build_path("/", path1, path2);
57
// file helpers -----------------------------
59
public bool file_or_dir_exists(string item_path){
61
/* check if item exists on disk*/
64
var item = File.parse_name(item_path);
65
return item.query_exists();
68
log_error (e.message);
73
public bool file_exists (string file_path){
74
/* Check if file exists */
75
return (FileUtils.test(file_path, GLib.FileTest.EXISTS)
76
&& !FileUtils.test(file_path, GLib.FileTest.IS_DIR));
79
public bool file_exists_regular (string file_path){
80
/* Check if file exists */
81
return ( FileUtils.test(file_path, GLib.FileTest.EXISTS)
82
&& FileUtils.test(file_path, GLib.FileTest.IS_REGULAR));
85
public bool file_delete(string file_path){
87
/* Check and delete file */
90
var file = File.new_for_path (file_path);
91
if (file.query_exists ()) {
96
log_error (e.message);
97
log_error(_("Failed to delete file") + ": %s".printf(file_path));
102
public bool file_move_to_trash(string file_path){
104
/* Check and delete file */
106
var file = File.new_for_path (file_path);
107
if (file.query_exists ()) {
108
Posix.system("gvfs-trash '%s'".printf(escape_single_quote(file_path)));
113
public bool file_shred(string file_path){
115
/* Check and delete file */
118
var file = File.new_for_path (file_path);
119
if (file.query_exists ()) {
120
Posix.system("shred -u '%s'".printf(escape_single_quote(file_path)));
125
log_error (e.message);
126
log_error(_("Failed to delete file") + ": %s".printf(file_path));
132
public string? file_read (string file_path){
134
/* Reads text from file */
140
GLib.FileUtils.get_contents (file_path, out txt, out size);
144
log_error (e.message);
145
log_error(_("Failed to read file") + ": %s".printf(file_path));
151
public bool file_write (string file_path, string contents){
153
/* Write text to file */
157
dir_create(file_parent(file_path));
159
var file = File.new_for_path (file_path);
160
if (file.query_exists ()) {
164
var file_stream = file.create (FileCreateFlags.REPLACE_DESTINATION);
165
var data_stream = new DataOutputStream (file_stream);
166
data_stream.put_string (contents);
171
log_error (e.message);
172
log_error(_("Failed to write file") + ": %s".printf(file_path));
177
public bool file_copy (string src_file, string dest_file){
179
var file_src = File.new_for_path (src_file);
180
if (file_src.query_exists()) {
181
var file_dest = File.new_for_path (dest_file);
182
file_src.copy(file_dest,FileCopyFlags.OVERWRITE,null,null);
187
log_error (e.message);
188
log_error(_("Failed to copy file") + ": '%s', '%s'".printf(src_file, dest_file));
194
public void file_move (string src_file, string dest_file){
196
var file_src = File.new_for_path (src_file);
197
if (file_src.query_exists()) {
198
var file_dest = File.new_for_path (dest_file);
199
file_src.move(file_dest,FileCopyFlags.OVERWRITE,null,null);
202
log_error(_("File not found") + ": '%s'".printf(src_file));
206
log_error (e.message);
207
log_error(_("Failed to move file") + ": '%s', '%s'".printf(src_file, dest_file));
212
// file info -----------------
214
public int64 file_get_size(string file_path){
216
File file = File.parse_name (file_path);
217
if (FileUtils.test(file_path, GLib.FileTest.EXISTS)){
218
if (FileUtils.test(file_path, GLib.FileTest.IS_REGULAR)
219
&& !FileUtils.test(file_path, GLib.FileTest.IS_SYMLINK)){
220
return file.query_info("standard::size",0).get_size();
225
log_error (e.message);
231
public DateTime file_get_modified_date(string file_path){
234
File file = File.parse_name (file_path);
235
if (file.query_exists()) {
236
info = file.query_info("%s".printf(FileAttribute.TIME_MODIFIED), 0);
237
return (new DateTime.from_timeval_utc(info.get_modification_time())).to_local();
241
log_error (e.message);
244
return (new DateTime.from_unix_utc(0)); //1970
247
public string file_get_symlink_target(string file_path){
250
File file = File.parse_name (file_path);
251
if (file.query_exists()) {
252
info = file.query_info("%s".printf(FileAttribute.STANDARD_SYMLINK_TARGET), 0);
253
return info.get_symlink_target();
257
log_error (e.message);
263
// directory helpers ----------------------
265
public bool dir_exists (string dir_path){
266
/* Check if directory exists */
267
return ( FileUtils.test(dir_path, GLib.FileTest.EXISTS) && FileUtils.test(dir_path, GLib.FileTest.IS_DIR));
270
public bool dir_create (string dir_path){
272
/* Creates a directory along with parents */
275
var dir = File.parse_name (dir_path);
276
if (dir.query_exists () == false) {
277
dir.make_directory_with_parents (null);
282
log_error (e.message);
283
log_error(_("Failed to create dir") + ": %s".printf(dir_path));
288
public bool dir_delete (string dir_path){
290
/* Recursively deletes directory along with contents */
292
string cmd = "rm -rf '%s'".printf(escape_single_quote(dir_path));
293
int status = exec_sync(cmd);
294
return (status == 0);
297
public bool dir_move_to_trash (string dir_path){
298
return file_move_to_trash(dir_path);
301
public bool dir_is_empty (string dir_path){
303
/* Check if directory is empty */
306
bool is_empty = true;
307
var dir = File.parse_name (dir_path);
308
if (dir.query_exists()) {
310
var enu = dir.enumerate_children ("%s".printf(FileAttribute.STANDARD_NAME), 0);
311
while ((info = enu.next_file()) != null) {
319
log_error (e.message);
325
public Gee.ArrayList<string> dir_list_names(string path){
326
var list = new Gee.ArrayList<string>();
330
File f_home = File.new_for_path (path);
331
FileEnumerator enumerator = f_home.enumerate_children ("%s".printf(FileAttribute.STANDARD_NAME), 0);
333
while ((file = enumerator.next_file ()) != null) {
334
string name = file.get_name();
339
log_error (e.message);
343
CompareDataFunc<string> entry_compare = (a, b) => {
346
list.sort((owned) entry_compare);
351
public bool dir_tar (string src_dir, string tar_file, bool recursion = true){
352
if (dir_exists(src_dir)) {
354
if (file_exists(tar_file)){
355
file_delete(tar_file);
358
var src_parent = file_parent(src_dir);
359
var src_name = file_basename(src_dir);
361
string cmd = "tar cvf '%s' --overwrite --%srecursion -C '%s' '%s'\n".printf(
362
escape_single_quote(tar_file),
363
(recursion ? "" : "no-"),
364
escape_single_quote(src_parent),
365
escape_single_quote(src_name));
369
string stdout, stderr;
370
int status = exec_script_sync(cmd, out stdout, out stderr);
379
log_error(_("Dir not found") + ": %s".printf(src_dir));
385
public bool dir_untar (string tar_file, string dst_dir){
386
if (file_exists(tar_file)) {
388
if (!dir_exists(dst_dir)){
392
string cmd = "tar xvf '%s' --overwrite --same-permissions -C '%s'\n".printf(
393
escape_single_quote(tar_file),
394
escape_single_quote(dst_dir));
398
string stdout, stderr;
399
int status = exec_script_sync(cmd, out stdout, out stderr);
408
log_error(_("File not found") + ": %s".printf(tar_file));
414
public bool chown(string dir_path, string user, string group = user){
415
string cmd = "chown %s:%s -R '%s'".printf(user, group, escape_single_quote(dir_path));
416
int status = exec_sync(cmd, null, null);
417
return (status == 0);
420
// dir info -------------------
422
// dep: find wc TODO: rewrite
423
public long dir_count(string path){
425
/* Return total count of files and directories */
432
cmd = "find '%s' | wc -l".printf(escape_single_quote(path));
433
ret_val = exec_script_sync(cmd, out std_out, out std_err);
434
return long.parse(std_out);
438
public long dir_size(string path){
440
/* Returns size of files and directories in KB*/
442
string cmd = "du -s -b '%s'".printf(escape_single_quote(path));
443
string std_out, std_err;
444
exec_sync(cmd, out std_out, out std_err);
445
return long.parse(std_out.split("\t")[0]);
449
public long dir_size_kb(string path){
451
/* Returns size of files and directories in KB*/
453
return (long)(dir_size(path) / 1024.0);
456
// archiving and encryption ----------------
459
public bool file_tar_encrypt (string src_file, string dst_file, string password){
460
if (file_exists(src_file)) {
461
if (file_exists(dst_file)){
462
file_delete(dst_file);
465
var src_dir = file_parent(src_file);
466
var src_name = file_basename(src_file);
468
var dst_dir = file_parent(dst_file);
469
var dst_name = file_basename(dst_file);
470
var tar_name = dst_name[0 : dst_name.index_of(".gpg")];
471
var tar_file = "%s/%s".printf(dst_dir, tar_name);
473
string cmd = "tar cvf '%s' --overwrite -C '%s' '%s'\n".printf(
474
escape_single_quote(tar_file),
475
escape_single_quote(src_dir),
476
escape_single_quote(src_name));
478
cmd += "gpg --passphrase '%s' -o '%s' --symmetric '%s'\n".printf(
480
escape_single_quote(dst_file),
481
escape_single_quote(tar_file));
483
cmd += "rm -f '%s'\n".printf(escape_single_quote(tar_file));
487
string stdout, stderr;
488
int status = exec_script_sync(cmd, out stdout, out stderr);
501
public string file_decrypt_untar_read (string src_file, string password){
503
if (file_exists(src_file)) {
505
//var src_name = file_basename(src_file);
506
//var tar_name = src_name[0 : src_name.index_of(".gpg")];
507
//var tar_file = "%s/%s".printf(TEMP_DIR, tar_name);
508
//var temp_file = "%s/%s".printf(TEMP_DIR, random_string());
512
cmd += "gpg --quiet --no-verbose --passphrase '%s' -o- --decrypt '%s'".printf(
514
escape_single_quote(src_file));
516
cmd += " | tar xf - --to-stdout 2>/dev/null\n";
521
string std_out, std_err;
522
int status = exec_script_sync(cmd, out std_out, out std_err);
532
log_error(_("File is missing") + ": %s".printf(src_file));
539
public bool decrypt_and_untar (string src_file, string dst_file, string password){
540
if (file_exists(src_file)) {
541
if (file_exists(dst_file)){
542
file_delete(dst_file);
545
var src_dir = file_parent(src_file);
546
var src_name = file_basename(src_file);
547
var tar_name = src_name[0 : src_name.index_of(".gpg")];
548
var tar_file = "%s/%s".printf(src_dir, tar_name);
552
// gpg cannot overwrite - remove tar file if it exists
553
cmd += "rm -f '%s'\n".printf(escape_single_quote(tar_file));
555
cmd += "gpg --passphrase '%s' -o '%s' --decrypt '%s'\n".printf(
557
escape_single_quote(tar_file),
558
escape_single_quote(src_file));
560
cmd += "status=$?; if [ $status -ne 0 ]; then exit $status; fi\n";
562
cmd += "tar xvf '%s' --overwrite --same-permissions -C '%s'\n".printf(
563
escape_single_quote(tar_file),
564
escape_single_quote(file_parent(dst_file)));
566
cmd += "rm -f '%s'\n".printf(escape_single_quote(tar_file));
570
string stdout, stderr;
571
int status = exec_script_sync(cmd, out stdout, out stderr);
581
log_error(_("File is missing") + ": %s".printf(src_file));
587
// hashing -----------
589
private string hash_md5(string path){
590
Checksum checksum = new Checksum (ChecksumType.MD5);
591
FileStream stream = FileStream.open (path, "rb");
595
while ((size = stream.read (fbuf)) > 0){
596
checksum.update (fbuf, size);
599
unowned string digest = checksum.get_string();
604
// misc --------------------
606
public string format_file_size (
607
uint64 size, bool binary_units = false, bool size_kb = false){
609
int64 unit_k = binary_units ? 1024 : 1000;
610
int64 unit_m = binary_units ? 1024 * unit_k : 1000 * unit_k;
611
int64 unit_g = binary_units ? 1024 * unit_m : 1000 * unit_m;
614
return "%'0.0f %sB".printf(size/(1.0 * unit_k), (binary_units)?"Ki":"K");
618
return "%'0.1f %sB".printf(size/(1.0 * unit_g), (binary_units)?"Gi":"G");
620
else if (size > unit_m){
621
return "%'0.1f %sB".printf(size/(1.0 * unit_m), (binary_units)?"Mi":"M");
623
else if (size > unit_k){
624
return "%'0.0f %sB".printf(size/(1.0 * unit_k), (binary_units)?"Ki":"K");
627
return "%'0lld B".printf(size);
631
public string escape_single_quote(string file_path){
632
return file_path.replace("'","'\\''");
637
public int chmod (string file, string permission){
639
/* Change file permissions */
640
string cmd = "chmod %s '%s'".printf(permission, escape_single_quote(file));
641
return exec_sync (cmd, null, null);
645
public string resolve_relative_path (string filePath){
647
/* Resolve the full path of given file using 'realpath' command */
649
string filePath2 = filePath;
650
if (filePath2.has_prefix ("~")){
651
filePath2 = Environment.get_home_dir () + "/" + filePath2[2:filePath2.length];
656
string cmd = "realpath '%s'".printf(escape_single_quote(filePath2));
657
Process.spawn_command_line_sync(cmd, out output);
658
output = output.strip ();
659
if (FileUtils.test(output, GLib.FileTest.EXISTS)){
664
log_error (e.message);
670
public int rsync (string sourceDirectory, string destDirectory, bool updateExisting, bool deleteExtra){
672
/* Sync files with rsync */
674
string cmd = "rsync -avh";
675
cmd += updateExisting ? "" : " --ignore-existing";
676
cmd += deleteExtra ? " --delete" : "";
677
cmd += " '%s'".printf(escape_single_quote(sourceDirectory) + "//");
678
cmd += " '%s'".printf(escape_single_quote(destDirectory));
679
return exec_sync (cmd, null, null);