source: filezilla/trunk/fuentes/src/engine/local_filesys.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

File size: 16.6 KB
Line 
1#include <filezilla.h>
2
3#include "local_filesys.h"
4#include <msgbox.h>
5
6#include <wx/dir.h>
7#include <wx/filename.h>
8#include <wx/msgdlg.h>
9
10#ifdef __WXMSW__
11const wxChar CLocalFileSystem::path_separator = '\\';
12#else
13const wxChar CLocalFileSystem::path_separator = '/';
14
15#include <utime.h>
16#endif
17
18namespace {
19template<typename T>
20int64_t make_int64_t(T hi, T lo)
21{
22        return (static_cast<int64_t>(hi) << 32) + static_cast<int64_t>(lo);
23}
24}
25
26CLocalFileSystem::~CLocalFileSystem()
27{
28        EndFindFiles();
29}
30
31CLocalFileSystem::local_fileType CLocalFileSystem::GetFileType(const wxString& path)
32{
33#ifdef __WXMSW__
34        DWORD result = GetFileAttributes(path);
35        if (result == INVALID_FILE_ATTRIBUTES)
36                return unknown;
37
38        if (result & FILE_ATTRIBUTE_REPARSE_POINT)
39                return link;
40
41        if (result & FILE_ATTRIBUTE_DIRECTORY)
42                return dir;
43
44        return file;
45#else
46        if (!path.empty() && path.Last() == '/' && path != _T("/")) {
47                wxString tmp = path;
48                tmp.RemoveLast();
49                return GetFileType(tmp);
50        }
51
52        wxStructStat buf;
53        int result = wxLstat(path, &buf);
54        if (result)
55                return unknown;
56
57#ifdef S_ISLNK
58        if (S_ISLNK(buf.st_mode))
59                return link;
60#endif
61
62        if (S_ISDIR(buf.st_mode))
63                return dir;
64
65        return file;
66#endif
67}
68
69bool CLocalFileSystem::RecursiveDelete(const wxString& path, wxWindow* parent)
70{
71        std::list<wxString> paths;
72        paths.push_back(path);
73        return RecursiveDelete(paths, parent);
74}
75
76bool CLocalFileSystem::RecursiveDelete(std::list<wxString> dirsToVisit, wxWindow* parent)
77{
78        // Under Windows use SHFileOperation to delete files and directories.
79        // Under other systems, we have to recurse into subdirectories manually
80        // to delete all contents.
81
82#ifdef __WXMSW__
83        // SHFileOperation accepts a list of null-terminated strings. Go through all
84        // paths to get the required buffer length
85
86        size_t len = 1; // String list terminated by empty string
87
88        for (auto const& dir : dirsToVisit) {
89                len += dir.size() + 1;
90        }
91
92        // Allocate memory
93        wxChar* pBuffer = new wxChar[len];
94        wxChar* p = pBuffer;
95
96        for (auto& dir : dirsToVisit) {
97                if (!dir.empty() && dir.Last() == wxFileName::GetPathSeparator())
98                        dir.RemoveLast();
99                if (GetFileType(dir) == unknown)
100                        continue;
101
102                _tcscpy(p, dir);
103                p += dir.size() + 1;
104        }
105        if (p != pBuffer) {
106                *p = 0;
107
108                // Now we can delete the files in the buffer
109                SHFILEOPSTRUCT op;
110                memset(&op, 0, sizeof(op));
111                op.hwnd = parent ? (HWND)parent->GetHandle() : 0;
112                op.wFunc = FO_DELETE;
113                op.pFrom = pBuffer;
114
115                if (parent) {
116                        // Move to trash if shift is not pressed, else delete
117                        op.fFlags = wxGetKeyState(WXK_SHIFT) ? 0 : FOF_ALLOWUNDO;
118                }
119                else
120                        op.fFlags = FOF_NOCONFIRMATION;
121
122                SHFileOperation(&op);
123        }
124        delete [] pBuffer;
125
126        return true;
127#else
128        if (parent) {
129                if (wxMessageBoxEx(_("Really delete all selected files and/or directories from your computer?"), _("Confirmation needed"), wxICON_QUESTION | wxYES_NO, parent) != wxYES)
130                        return true;
131        }
132
133        for (auto& dir : dirsToVisit) {
134                if (!dir.empty() && dir.Last() == '/' && dir != _T("/"))
135                        dir.RemoveLast();
136        }
137
138        bool encodingError = false;
139
140        // Remember the directories to delete after recursing into them
141        std::list<wxString> dirsToDelete;
142
143        CLocalFileSystem fs;
144
145        // Process all dirctories that have to be visited
146        while (!dirsToVisit.empty()) {
147                auto const iter = dirsToVisit.begin();
148                wxString const& path = *iter;
149
150                if (GetFileType(path) != dir) {
151                        wxRemoveFile(path);
152                        dirsToVisit.erase(iter);
153                        continue;
154                }
155
156                dirsToDelete.splice(dirsToDelete.begin(), dirsToVisit, iter);
157
158                if (!fs.BeginFindFiles(path, false)) {
159                        continue;
160                }
161
162                // Depending on underlying platform, wxDir does not handle
163                // changes to the directory contents very well.
164                // See https://trac.filezilla-project.org/ticket/3482
165                // To work around this, delete files after enumerating everything in current directory
166                std::list<wxString> filesToDelete;
167
168                wxString file;
169                while (fs.GetNextFile(file)) {
170                        if (file.empty()) {
171                                encodingError = true;
172                                continue;
173                        }
174
175                        const wxString& fullName = path + _T("/") + file;
176
177                        if (CLocalFileSystem::GetFileType(fullName) == CLocalFileSystem::dir)
178                                dirsToVisit.push_back(fullName);
179                        else
180                                filesToDelete.push_back(fullName);
181                }
182                fs.EndFindFiles();
183
184                // Delete all files and links in current directory enumerated before
185                for (auto const& file : filesToDelete) {
186                        wxRemoveFile(file);
187                }
188        }
189
190        // Delete the now empty directories
191        for (auto const& dir : dirsToDelete) {
192                wxRmdir(dir);
193        }
194
195        return !encodingError;
196#endif
197}
198
199CLocalFileSystem::local_fileType CLocalFileSystem::GetFileInfo(const wxString& path, bool &isLink, int64_t* size, CDateTime* modificationTime, int *mode)
200{
201#ifdef __WXMSW__
202        if (!path.empty() && path.Last() == wxFileName::GetPathSeparator() && path != wxFileName::GetPathSeparator()) {
203                wxString tmp = path;
204                tmp.RemoveLast();
205                return GetFileInfo(tmp, isLink, size, modificationTime, mode);
206        }
207
208        isLink = false;
209
210        WIN32_FILE_ATTRIBUTE_DATA attributes;
211        BOOL result = GetFileAttributesEx(path, GetFileExInfoStandard, &attributes);
212        if (!result) {
213                if (size)
214                        *size = -1;
215                if (mode)
216                        *mode = 0;
217                if (modificationTime)
218                        *modificationTime = CDateTime();
219                return unknown;
220        }
221
222        bool is_dir = (attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
223
224        if (attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
225                isLink = true;
226
227                HANDLE hFile = is_dir ? INVALID_HANDLE_VALUE : CreateFile(path, FILE_READ_ATTRIBUTES | FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
228                if (hFile != INVALID_HANDLE_VALUE) {
229                        BY_HANDLE_FILE_INFORMATION info{};
230                        int ret = GetFileInformationByHandle(hFile, &info);
231                        CloseHandle(hFile);
232                        if (ret != 0 && !(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
233
234                                if (modificationTime) {
235                                        if (!modificationTime->Set(info.ftLastWriteTime, CDateTime::milliseconds)) {
236                                                modificationTime->Set(info.ftCreationTime, CDateTime::milliseconds);
237                                        }
238                                }
239
240                                if (mode)
241                                        *mode = (int)info.dwFileAttributes;
242
243                                if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
244                                        if (size)
245                                                *size = -1;
246                                        return dir;
247                                }
248
249                                if (size)
250                                        *size = make_int64_t(info.nFileSizeHigh, info.nFileSizeLow);
251
252                                return file;
253                        }
254                }
255
256                if (size)
257                        *size = -1;
258                if (mode)
259                        *mode = 0;
260                if (modificationTime)
261                        *modificationTime = CDateTime();
262                return is_dir ? dir : unknown;
263        }
264
265        if (modificationTime) {
266                *modificationTime = CDateTime(attributes.ftLastWriteTime, CDateTime::milliseconds);
267                if (!modificationTime->IsValid()) {
268                        *modificationTime = CDateTime(attributes.ftCreationTime, CDateTime::milliseconds);
269                }
270        }
271
272        if (mode)
273                *mode = (int)attributes.dwFileAttributes;
274
275        if (is_dir) {
276                if (size)
277                        *size = -1;
278                return dir;
279        }
280        else {
281                if (size)
282                        *size = make_int64_t(attributes.nFileSizeHigh, attributes.nFileSizeLow);
283                return file;
284        }
285#else
286        if (!path.empty() && path.Last() == '/' && path != _T("/")) {
287                wxString tmp = path;
288                tmp.RemoveLast();
289                return GetFileInfo(tmp, isLink, size, modificationTime, mode);
290        }
291
292        const wxCharBuffer p = path.fn_str();
293        return GetFileInfo((const char*)p, isLink, size, modificationTime, mode);
294#endif
295}
296
297#ifndef __WXMSW__
298CLocalFileSystem::local_fileType CLocalFileSystem::GetFileInfo(const char* path, bool &isLink, int64_t* size, CDateTime* modificationTime, int *mode)
299{
300        struct stat buf;
301        int result = lstat(path, &buf);
302        if (result)
303        {
304                isLink = false;
305                if (size)
306                        *size = -1;
307                if (mode)
308                        *mode = -1;
309                if (modificationTime)
310                        *modificationTime = CDateTime();
311                return unknown;
312        }
313
314#ifdef S_ISLNK
315        if (S_ISLNK(buf.st_mode))
316        {
317                isLink = true;
318                int result = stat(path, &buf);
319                if (result)
320                {
321                        if (size)
322                                *size = -1;
323                        if (mode)
324                                *mode = -1;
325                        if (modificationTime)
326                                *modificationTime = CDateTime();
327                        return unknown;
328                }
329        }
330        else
331#endif
332                isLink = false;
333
334        if (modificationTime)
335                *modificationTime = CDateTime(buf.st_mtime, CDateTime::seconds);
336
337        if (mode)
338                *mode = buf.st_mode & 0x777;
339
340        if (S_ISDIR(buf.st_mode))
341        {
342                if (size)
343                        *size = -1;
344                return dir;
345        }
346
347        if (size)
348                *size = buf.st_size;
349
350        return file;
351}
352#endif
353
354bool CLocalFileSystem::BeginFindFiles(wxString path, bool dirs_only)
355{
356        if (path.empty()) {
357                return false;
358        }
359
360        EndFindFiles();
361
362        m_dirs_only = dirs_only;
363#ifdef __WXMSW__
364        if (path.Last() != '/' && path.Last() != '\\') {
365                m_find_path = path + _T("\\");
366                path += _T("\\*");
367        }
368        else {
369                m_find_path = path;
370                path += '*';
371        }
372
373        m_hFind = FindFirstFileEx(path, FindExInfoStandard, &m_find_data, dirs_only ? FindExSearchLimitToDirectories : FindExSearchNameMatch, 0, 0);
374        if (m_hFind == INVALID_HANDLE_VALUE) {
375                m_found = false;
376                return false;
377        }
378
379        m_found = true;
380        return true;
381#else
382        if (path != _T("/") && path.Last() == '/')
383                path.RemoveLast();
384
385        const wxCharBuffer s = path.fn_str();
386
387        m_dir = opendir(s);
388        if (!m_dir)
389                return false;
390
391        const wxCharBuffer p = path.fn_str();
392        const int len = strlen(p);
393        m_raw_path = new char[len + 2048 + 2];
394        m_buffer_length = len + 2048 + 2;
395        strcpy(m_raw_path, p);
396        if (len > 1)
397        {
398                m_raw_path[len] = '/';
399                m_file_part = m_raw_path + len + 1;
400        }
401        else
402                m_file_part = m_raw_path + len;
403
404        return true;
405#endif
406}
407
408void CLocalFileSystem::EndFindFiles()
409{
410#ifdef __WXMSW__
411        m_found = false;
412        if (m_hFind != INVALID_HANDLE_VALUE)
413        {
414                FindClose(m_hFind);
415                m_hFind = INVALID_HANDLE_VALUE;
416        }
417#else
418        if (m_dir)
419        {
420                closedir(m_dir);
421                m_dir = 0;
422        }
423        delete [] m_raw_path;
424        m_raw_path = 0;
425        m_file_part = 0;
426#endif
427}
428
429bool CLocalFileSystem::GetNextFile(wxString& name)
430{
431#ifdef __WXMSW__
432        if (!m_found)
433                return false;
434        do {
435                name = m_find_data.cFileName;
436                if (name.empty()) {
437                        m_found = FindNextFile(m_hFind, &m_find_data) != 0;
438                        return true;
439                }
440                if (name == _T(".") || name == _T(".."))
441                        continue;
442
443                if (m_dirs_only && !(m_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
444                        continue;
445
446                m_found = FindNextFile(m_hFind, &m_find_data) != 0;
447                return true;
448        } while ((m_found = FindNextFile(m_hFind, &m_find_data) != 0));
449
450        return false;
451#else
452        if (!m_dir)
453                return false;
454
455        struct dirent* entry;
456        while ((entry = readdir(m_dir))) {
457                if (!entry->d_name[0] ||
458                        !strcmp(entry->d_name, ".") ||
459                        !strcmp(entry->d_name, ".."))
460                        continue;
461
462                if (m_dirs_only) {
463#if HAVE_STRUCT_DIRENT_D_TYPE
464                        if (entry->d_type == DT_LNK)
465                        {
466                                bool wasLink;
467                                AllocPathBuffer(entry->d_name);
468                                strcpy(m_file_part, entry->d_name);
469                                if (GetFileInfo(m_raw_path, wasLink, 0, 0, 0) != dir)
470                                        continue;
471                        }
472                        else if (entry->d_type != DT_DIR)
473                                continue;
474#else
475                        // Solaris doesn't have d_type
476                        bool wasLink;
477                        AllocPathBuffer(entry->d_name);
478                        strcpy(m_file_part, entry->d_name);
479                        if (GetFileInfo(m_raw_path, wasLink, 0, 0, 0) != dir)
480                                continue;
481#endif
482                }
483
484                name = wxString(entry->d_name, *wxConvFileName);
485
486                return true;
487        }
488
489        return false;
490#endif
491}
492
493bool CLocalFileSystem::GetNextFile(wxString& name, bool &isLink, bool &is_dir, int64_t* size, CDateTime* modificationTime, int* mode)
494{
495#ifdef __WXMSW__
496        if (!m_found)
497                return false;
498        do
499        {
500                if (!m_find_data.cFileName[0]) {
501                        m_found = FindNextFile(m_hFind, &m_find_data) != 0;
502                        return true;
503                }
504                if (m_dirs_only && !(m_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
505                        continue;
506
507                if (m_find_data.cFileName[0] == '.' && (!m_find_data.cFileName[1] || (m_find_data.cFileName[1] == '.' && !m_find_data.cFileName[2])))
508                        continue;
509                name = m_find_data.cFileName;
510
511                is_dir = (m_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
512
513                isLink = (m_find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
514                if (isLink) {
515                        HANDLE hFile = is_dir ? INVALID_HANDLE_VALUE : CreateFile(m_find_path + name, FILE_READ_ATTRIBUTES | FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
516                        if (hFile != INVALID_HANDLE_VALUE) {
517                                BY_HANDLE_FILE_INFORMATION info{};
518                                int ret = GetFileInformationByHandle(hFile, &info);
519                                CloseHandle(hFile);
520                                if (ret != 0 && !(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
521
522                                        if (modificationTime) {
523                                                *modificationTime = CDateTime(info.ftLastWriteTime, CDateTime::milliseconds);
524                                                if (!modificationTime->IsValid()) {
525                                                        *modificationTime = CDateTime(info.ftCreationTime, CDateTime::milliseconds);
526                                                }
527                                        }
528
529                                        if (mode)
530                                                *mode = (int)info.dwFileAttributes;
531
532                                        is_dir = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
533                                        if (size) {
534                                                if (is_dir) {
535                                                        *size = -1;
536                                                }
537                                                else {
538                                                        *size = make_int64_t(info.nFileSizeHigh, info.nFileSizeLow);
539                                                }
540                                        }
541
542                                        m_found = FindNextFile(m_hFind, &m_find_data) != 0;
543                                        return true;
544                                }
545                        }
546
547                        if (m_dirs_only && !is_dir) {
548                                continue;
549                        }
550
551                        if (size)
552                                *size = -1;
553                        if (mode)
554                                *mode = 0;
555                        if (modificationTime)
556                                *modificationTime = CDateTime();
557                }
558                else {
559                        if (modificationTime) {
560                                *modificationTime = CDateTime(m_find_data.ftLastWriteTime, CDateTime::milliseconds);
561                                if (!modificationTime->IsValid()) {
562                                        *modificationTime = CDateTime(m_find_data.ftLastWriteTime, CDateTime::milliseconds);
563                                }
564                        }
565
566                        if (mode)
567                                *mode = (int)m_find_data.dwFileAttributes;
568
569                        if (size) {
570                                if (is_dir) {
571                                        *size = -1;
572                                }
573                                else {
574                                        *size = make_int64_t(m_find_data.nFileSizeHigh, m_find_data.nFileSizeLow);
575                                }
576                        }
577                }
578                m_found = FindNextFile(m_hFind, &m_find_data) != 0;
579                return true;
580        } while ((m_found = FindNextFile(m_hFind, &m_find_data) != 0));
581
582        return false;
583#else
584        if (!m_dir)
585                return false;
586
587        struct dirent* entry;
588        while ((entry = readdir(m_dir)))
589        {
590                if (!entry->d_name[0] ||
591                        !strcmp(entry->d_name, ".") ||
592                        !strcmp(entry->d_name, ".."))
593                        continue;
594
595#if HAVE_STRUCT_DIRENT_D_TYPE
596                if (m_dirs_only)
597                {
598                        if (entry->d_type == DT_LNK)
599                        {
600                                AllocPathBuffer(entry->d_name);
601                                strcpy(m_file_part, entry->d_name);
602                                local_fileType type = GetFileInfo(m_raw_path, isLink, size, modificationTime, mode);
603                                if (type != dir)
604                                        continue;
605
606                                name = wxString(entry->d_name, *wxConvFileName);
607                                is_dir = true;
608                                return true;
609                        }
610                        else if (entry->d_type != DT_DIR)
611                                continue;
612                }
613#endif
614
615                AllocPathBuffer(entry->d_name);
616                strcpy(m_file_part, entry->d_name);
617                local_fileType type = GetFileInfo(m_raw_path, isLink, size, modificationTime, mode);
618
619                if (type == unknown) // Happens for example in case of permission denied
620                {
621#if HAVE_STRUCT_DIRENT_D_TYPE
622                        type = entry->d_type == DT_DIR ? dir : file;
623#else
624                        type = file;
625#endif
626                        isLink = 0;
627                        if (size)
628                                *size = -1;
629                        if (modificationTime)
630                                *modificationTime = CDateTime();
631                        if (mode)
632                                *mode = 0;
633                }
634                if (m_dirs_only && type != dir)
635                        continue;
636
637                is_dir = type == dir;
638
639                name = wxString(entry->d_name, *wxConvFileName);
640
641                return true;
642        }
643
644        return false;
645#endif
646}
647
648#ifndef __WXMSW__
649void CLocalFileSystem::AllocPathBuffer(const char* file)
650{
651        int len = strlen(file);
652        int pathlen = m_file_part - m_raw_path;
653
654        if (len + pathlen >= m_buffer_length)
655        {
656                m_buffer_length = (len + pathlen) * 2;
657                char* tmp = new char[m_buffer_length];
658                memcpy(tmp, m_raw_path, pathlen);
659                delete [] m_raw_path;
660                m_raw_path = tmp;
661                m_file_part = m_raw_path + pathlen;
662        }
663}
664#endif
665
666CDateTime CLocalFileSystem::GetModificationTime( const wxString& path)
667{
668        CDateTime mtime;
669
670        bool tmp;
671        if (GetFileInfo(path, tmp, 0, &mtime, 0) == unknown)
672                mtime = CDateTime();
673
674        return mtime;
675}
676
677bool CLocalFileSystem::SetModificationTime(const wxString& path, const CDateTime& t)
678{
679        if (!t.IsValid())
680                return false;
681
682#ifdef __WXMSW__
683        FILETIME ft = t.GetFileTime();
684        if (!ft.dwHighDateTime) {
685                return false;
686        }
687
688        HANDLE h = CreateFile(path, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
689        if (h == INVALID_HANDLE_VALUE)
690                return false;
691
692        bool ret = SetFileTime(h, 0, &ft, &ft) == TRUE;
693        CloseHandle(h);
694        return ret;
695#else
696        utimbuf utm{};
697        utm.actime = t.GetTimeT();
698        utm.modtime = utm.actime;
699        return utime(path.fn_str(), &utm) == 0;
700#endif
701}
702
703int64_t CLocalFileSystem::GetSize(wxString const& path, bool* isLink)
704{
705        int64_t ret = -1;
706        bool tmp{};
707        local_fileType t = GetFileInfo(path, isLink ? *isLink : tmp, &ret, 0, 0);
708        if( t != file ) {
709                ret = -1;
710        }
711
712        return ret;
713}
714
715wxString CLocalFileSystem::GetSymbolicLinkTarget(wxString const& path)
716{
717        wxString target;
718
719#ifdef __WXMSW__
720        HANDLE hFile = CreateFile(path, FILE_READ_ATTRIBUTES | FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
721        if (hFile != INVALID_HANDLE_VALUE) {
722                DWORD const size = 1024;
723                wxChar out[size];
724                DWORD ret = GetFinalPathNameByHandle(hFile, out, size, 0);
725                if (ret > 0 && ret < size) {
726                        target = out;
727                }
728                CloseHandle(hFile);
729        }
730#else
731        size_t const size = 1024;
732        char out[size];
733
734        const wxCharBuffer p = path.fn_str();
735        ssize_t res = readlink(static_cast<char const*>(p), out, size);
736        if( res > 0 && static_cast<size_t>(res) < size ) {
737                out[res] = 0;
738                target = wxString(out, *wxConvFileName);
739        }
740#endif
741        return target;
742}
Note: See TracBrowser for help on using the repository browser.