1 /*
2 * Part of WCM Commander
3 * https://github.com/corporateshark/WCMCommander
4 * wcm@linderdaum.com
5 */
6
7 #include "vfs.h"
8 #include "string-util.h"
9 #include <sys/types.h>
10 #include "unicode_lc.h"
11 #include <algorithm>
12
13 #if defined( __APPLE__ )
14 # include <sys/param.h>
15 # include <sys/mount.h>
16 #endif
17
18 #include <unordered_map>
19
20 /////////////////////////////////////////////////// FS /////////////////////////////
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)21 int FS::OpenRead ( FSPath& path, int flags, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)22 int FS::OpenCreate ( FSPath& path, bool overwrite, int mode, int flags, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
Close(int fd,int * err,FSCInfo * info)23 int FS::Close ( int fd, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
Read(int fd,void * buf,int size,int * err,FSCInfo * info)24 int FS::Read ( int fd, void* buf, int size, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
Write(int fd,void * buf,int size,int * err,FSCInfo * info)25 int FS::Write ( int fd, void* buf, int size, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)26 int FS::Seek ( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)27 int FS::Rename ( FSPath& oldpath, FSPath& newpath, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)28 int FS::MkDir ( FSPath& path, int mode, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
Delete(FSPath & path,int * err,FSCInfo * info)29 int FS::Delete ( FSPath& path, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
RmDir(FSPath & path,int * err,FSCInfo * info)30 int FS::RmDir ( FSPath& path, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)31 int FS::SetFileTime ( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
ReadDir(FSList * list,FSPath & path,int * err,FSCInfo * info)32 int FS::ReadDir ( FSList* list, FSPath& path, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
Stat(FSPath & path,FSStat * st,int * err,FSCInfo * info)33 int FS::Stat( FSPath& path, FSStat* st, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
StatSetAttr(FSPath & path,const FSStat * st,int * err,FSCInfo * info)34 int FS::StatSetAttr( FSPath& path, const FSStat* st, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
FStat(int fd,FSStat * st,int * err,FSCInfo * info)35 int FS::FStat( int fd, FSStat* st, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)36 int FS::Symlink ( FSPath& path, FSString& str, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
StatVfs(FSPath & path,FSStatVfs * st,int * err,FSCInfo * info)37 int FS::StatVfs( FSPath& path, FSStatVfs* st, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
GetFileSystemFreeSpace(FSPath & path,int * err)38 int64_t FS::GetFileSystemFreeSpace( FSPath& path, int* err ) { SetError( err, 0 ); return -1; }
39
GetUserName(int user,unicode_t buf[64])40 unicode_t* FS::GetUserName( int user, unicode_t buf[64] ) { buf[0] = 0; return buf; };
GetGroupName(int group,unicode_t buf[64])41 unicode_t* FS::GetGroupName( int group, unicode_t buf[64] ) { buf[0] = 0; return buf; };
42
~FS()43 FS::~FS() {}
44
45 ////////////////////////////////////// FSCInfo
SmbLogon(FSSmbParam * a)46 bool FSCInfo::SmbLogon( FSSmbParam* a ) { return false; }
FtpLogon(FSFtpParam * a)47 bool FSCInfo::FtpLogon( FSFtpParam* a ) { return false; }
IsStopped() const48 bool FSCInfo::IsStopped() const { return false; }
Prompt(const unicode_t * header,const unicode_t * message,FSPromptData * p,int count)49 bool FSCInfo::Prompt( const unicode_t* header, const unicode_t* message, FSPromptData* p, int count ) { return false; }
~FSCInfo()50 FSCInfo::~FSCInfo() {}
51
52 ////////////////////////////////////// FSCSimpleInfo
IsStopped() const53 bool FSCSimpleInfo::IsStopped() const { return m_Stopped; }
~FSCSimpleInfo()54 FSCSimpleInfo::~FSCSimpleInfo() {}
55
56 ////////////////////////////////////////////////// FSSys ///////////////////////////
57
58
59 #ifdef _WIN32
60 //#error
61 #include <time.h>
62
63
TT_to_FILETIME(time_t t,FILETIME & ft)64 static void TT_to_FILETIME( time_t t, FILETIME& ft )
65 {
66 LONGLONG ll;
67 ll = Int32x32To64( t, 10000000 ) + 116444736000000000ll;
68 ft.dwLowDateTime = ( DWORD )(ll & 0xFFFFFFFF );
69 ft.dwHighDateTime = ( DWORD )(( ll >> 32 ) & 0xFFFFFFFF );
70 }
71
72 //!!! Херня какая-то в преобразовании, надо разбираться
FILETIME_to_TT(FILETIME ft)73 static time_t FILETIME_to_TT( FILETIME ft )
74 {
75 LONGLONG ll = ft.dwHighDateTime;
76 ll <<= 32;
77 ll += ft.dwHighDateTime;
78 ll -= 116444736000000000ll;
79
80 if ( ll < 0 ) { return 0; }
81
82 ll /= 10000000;
83 return ll;
84 }
85
GetTimeT()86 time_t FSTime::GetTimeT()
87 {
88 if ( flags & TIME_T_OK ) { return tt; }
89
90 if ( ( flags & FILETIME_OK ) == 0 ) { return 0; }
91
92 tt = FILETIME_to_TT( ft );
93 flags |= TIME_T_OK;
94 return tt;
95 }
96
GetFileTime()97 FILETIME FSTime::GetFileTime()
98 {
99 if ( flags & FILETIME_OK ) { return ft; }
100
101 static FILETIME t0 = {0, 0};
102
103 if ( ( flags & TIME_T_OK ) == 0 ) { return t0; }
104
105 TT_to_FILETIME( tt, ft );
106 flags |= FILETIME_OK;
107 return ft;
108 }
109
Utf16Chars(const wchar_t * s)110 inline int Utf16Chars( const wchar_t* s ) { int n = 0; for ( ; *s; s++ ) { n++; } return n; }
111
UnicodeToUtf16_cat(const unicode_t * s,wchar_t * cat)112 inline std::vector<wchar_t> UnicodeToUtf16_cat( const unicode_t* s, wchar_t* cat )
113 {
114 int lcat = Utf16Chars( cat );
115 std::vector<wchar_t> p( unicode_strlen( s ) + lcat + 1 );
116 wchar_t* d;
117
118 for ( d = p.data(); *s; s++, d++ ) { *d = *s; }
119
120 for ( ; *cat; cat++, d++ ) { *d = *cat; }
121
122 *d = 0;
123 return p;
124 }
125
126
127
Flags()128 unsigned FSSys::Flags() { return HAVE_READ | HAVE_WRITE | HAVE_SYMLINK | HAVE_SEEK; }
IsEEXIST(int err)129 bool FSSys::IsEEXIST( int err ) { return err == ERROR_FILE_EXISTS; } //err == EEXIST; }
IsENOENT(int err)130 bool FSSys::IsENOENT( int err ) { return err == ERROR_FILE_NOT_FOUND ; } // err == ENOENT; }
IsEXDEV(int err)131 bool FSSys::IsEXDEV( int err ) { return false; } // err == EXDEV; }
132
StrError(int err)133 FSString FSSys::StrError( int err )
134 {
135 sys_char_t buf[1024];
136 FSString ret;
137 ret.SetSys( sys_error_str( err, buf, 0x100 ) );
138 return ret;
139 }
140
Equal(FS * fs)141 bool FSSys::Equal( FS* fs )
142 {
143 if ( !fs || fs->Type() != FS::SYSTEM
144 #ifdef _WIN32
145 || _drive != ((FSSys*)fs)->_drive
146 #endif
147 ) { return false; }
148
149 return true;
150 }
151
152 /* не поддерживает пути >265 длиной
153 static std::vector<wchar_t> SysPathStr(int drive, const unicode_t *s)
154 {
155 std::vector<wchar_t> p(2 + unicode_strlen(s)+1);
156 wchar_t *d =p.ptr();
157
158 if (drive == -1) { //???
159 *(d++) = '\\';
160 } else {
161 d[0]=drive+'a';
162 d[1]=':';
163 d+=2;
164 }
165
166 for (; *s; s++, d++) *d = *s;
167 *d=0;
168 return p;
169 }
170 */
171
SysPathStr(int drive,const unicode_t * s)172 static std::vector<wchar_t> SysPathStr( int drive, const unicode_t* s )
173 {
174 std::vector<wchar_t> p( 10 + unicode_strlen( s ) + 1 ); //+10 прозапас, вообще +8 (макс \\?\UNC\)
175 wchar_t* d = p.data();
176
177 d[0] = '\\';
178 d[1] = '\\';
179 d[2] = '?';
180 d[3] = '\\';
181 d += 4;
182
183 if ( drive == -1 ) //???
184 {
185 d[0] = 'U';
186 d[1] = 'N';
187 d[2] = 'C';
188 //d[3]='\\'; //еше \ добавится из пути
189 d += 3;
190 }
191 else
192 {
193 d[0] = drive + 'a';
194 d[1] = ':';
195 d += 2;
196 }
197
198 for ( ; *s; s++, d++ ) { *d = *s; }
199
200 *d = 0;
201 return p;
202 }
203
204
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)205 int FSSys::OpenRead ( FSPath& path, int flags, int* err, FSCInfo* info )
206 {
207 int shareFlags = 0;
208
209 if ( flags & FS::SHARE_READ ) { shareFlags |= FILE_SHARE_READ; }
210
211 if ( flags & FS::SHARE_WRITE ) { shareFlags |= FILE_SHARE_WRITE; }
212
213 HANDLE h = CreateFileW( SysPathStr( _drive, path.GetUnicode( '\\' ) ).data(), GENERIC_READ, shareFlags, 0, OPEN_EXISTING, 0, 0 );
214
215 //file_open(SysPathStr(_drive, path.GetUnicode('\\')).ptr());
216 if ( h == INVALID_HANDLE_VALUE )
217 {
218 SetError( err, GetLastError() );
219 return -1;
220 }
221
222 int fd = handles.New();
223 HANDLE* p = this->handles.Handle( fd );
224
225 if ( !p )
226 {
227 SetError( err, ERROR_INVALID_PARAMETER );
228 CloseHandle( h );
229 return -1;
230 }
231
232 *p = h;
233 return fd;
234 }
235
236
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)237 int FSSys::OpenCreate ( FSPath& path, bool overwrite, int mode, int flags, int* err, FSCInfo* info )
238 {
239 DWORD diseredAccess = GENERIC_READ | GENERIC_WRITE;
240 // DWORD shareMode = 0;
241 DWORD creationDisposition = ( overwrite ) ? CREATE_ALWAYS : CREATE_NEW;
242 //???
243
244 HANDLE h = CreateFileW( SysPathStr( _drive, path.GetUnicode( '\\' ) ).data(), diseredAccess, FILE_SHARE_WRITE, 0, creationDisposition, 0, 0 );
245
246 if ( h == INVALID_HANDLE_VALUE )
247 {
248 SetError( err, GetLastError() );
249 return -1;
250 }
251
252 int fd = handles.New();
253 HANDLE* p = this->handles.Handle( fd );
254
255 if ( !p )
256 {
257 SetError( err, ERROR_INVALID_PARAMETER );
258 CloseHandle( h );
259 return -1;
260 }
261
262 *p = h;
263 return fd;
264 }
265
Close(int fd,int * err,FSCInfo * info)266 int FSSys::Close( int fd, int* err, FSCInfo* info )
267 {
268 HANDLE* p = this->handles.Handle( fd );
269
270 if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
271
272 HANDLE h = *p;
273 handles.Free( fd );
274
275 if ( !CloseHandle( h ) )
276 {
277 SetError( err, GetLastError() );
278 return -1;
279 }
280
281 return 0;
282 }
283
Read(int fd,void * buf,int size,int * err,FSCInfo * info)284 int FSSys::Read( int fd, void* buf, int size, int* err, FSCInfo* info )
285 {
286 HANDLE* p = this->handles.Handle( fd );
287
288 if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
289
290 DWORD bytes;
291
292 if ( !ReadFile( *p, buf, size, &bytes, 0 ) )
293 {
294 SetError( err, GetLastError() );
295 return -1;
296 }
297
298 return bytes;
299 }
300
Write(int fd,void * buf,int size,int * err,FSCInfo * info)301 int FSSys::Write( int fd, void* buf, int size, int* err, FSCInfo* info )
302 {
303 HANDLE* p = this->handles.Handle( fd );
304
305 if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
306
307 DWORD bytes;
308
309 if ( !WriteFile( *p, buf, size, &bytes, 0 ) )
310 {
311 SetError( err, GetLastError() );
312 return -1;
313 }
314
315 return bytes;
316 }
317
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)318 int FSSys::Seek( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet, int* err, FSCInfo* info )
319 {
320 HANDLE* p = this->handles.Handle( fd );
321
322 if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
323
324 LARGE_INTEGER li;
325 li.QuadPart = pos;
326
327 if ( !SetFilePointerEx( *p, li, &li, mode ) )
328 {
329 SetError( err, GetLastError() );
330 return -1;
331 }
332
333 if ( pRet )
334 {
335 *pRet = li.QuadPart;
336 }
337
338 return 0;
339 }
340
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)341 int FSSys::Rename ( FSPath& oldpath, FSPath& newpath, int* err, FSCInfo* info )
342 {
343 if ( MoveFileW(
344 SysPathStr( _drive, oldpath.GetUnicode( '\\' ) ).data(),
345 SysPathStr( _drive, newpath.GetUnicode( '\\' ) ).data()
346 ) ) { return 0; }
347
348 SetError( err, GetLastError() );
349 return -1;
350 }
351
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)352 int FSSys::MkDir( FSPath& path, int mode, int* err, FSCInfo* info )
353 {
354 if ( CreateDirectoryW( SysPathStr( _drive, path.GetUnicode( '\\' ) ).data(), 0 ) ) { return 0; }
355
356 DWORD e = GetLastError();
357
358 if ( e == ERROR_ALREADY_EXISTS ) { return 0; }
359
360 SetError( err, e );
361 return -1;
362 }
363
Delete(FSPath & path,int * err,FSCInfo * info)364 int FSSys::Delete( FSPath& path, int* err, FSCInfo* info )
365 {
366 std::vector<wchar_t> sp = SysPathStr( _drive, path.GetUnicode( '\\' ) );
367
368 if ( DeleteFileW( sp.data() ) ) { return 0; }
369
370 DWORD lastError = GetLastError();
371
372 if ( lastError == ERROR_ACCESS_DENIED ) //возможно read only аттрибут, пытаемся сбросить
373 {
374 if ( SetFileAttributesW( sp.data(), 0 ) && DeleteFileW( sp.data() ) ) { return 0; }
375
376 lastError = GetLastError();
377 }
378
379 SetError( err, lastError );
380 return -1;
381 }
382
RmDir(FSPath & path,int * err,FSCInfo * info)383 int FSSys::RmDir( FSPath& path, int* err, FSCInfo* info )
384 {
385 std::vector<wchar_t> sp = SysPathStr( _drive, path.GetUnicode( '\\' ) );
386
387 if ( RemoveDirectoryW( sp.data() ) ) { return 0; }
388
389 DWORD lastError = GetLastError();
390
391 if ( lastError == ERROR_ACCESS_DENIED ) //возможно read only аттрибут, пытаемся сбросить
392 {
393 if ( SetFileAttributesW( sp.data(), 0 ) && RemoveDirectoryW( sp.data() ) ) { return 0; }
394
395 lastError = GetLastError();
396 }
397
398 SetError( err, lastError );
399 return -1;
400 }
401
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)402 int FSSys::SetFileTime ( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info )
403 {
404 HANDLE h = CreateFileW( SysPathStr( _drive, path.GetUnicode( '\\' ) ).data(), FILE_WRITE_ATTRIBUTES , 0, 0, OPEN_EXISTING, 0, 0 );
405
406 if ( h == INVALID_HANDLE_VALUE )
407 {
408 SetError( err, GetLastError() );
409 return -1;
410 }
411
412 FILETIME ct = cTime;
413 FILETIME at = aTime;
414 FILETIME mt = mTime;
415
416 if ( !::SetFileTime( h, &ct, &at, &mt ) )
417 {
418 SetError( err, GetLastError() );
419 CloseHandle( h );
420 return -1;
421 }
422
423 CloseHandle( h );
424 return 0;
425 }
426 /* не поддерживает пути >265 длиной
427 static std::vector<wchar_t> FindPathStr(int drive, const unicode_t *s, wchar_t *cat)
428 {
429 int lcat = Utf16Chars(cat);
430
431 std::vector<wchar_t> p(2 + unicode_strlen(s)+lcat+1);
432 wchar_t *d =p.ptr();
433
434 if (drive == -1) {
435 *(d++) = '\\';
436 } else {
437 d[0]=drive+'a';
438 d[1]=':';
439 d+=2;
440 }
441
442 for (; *s; s++, d++) *d = *s;
443 for (;*cat;cat++, d++) *d=*cat;
444 *d=0;
445 return p;
446 }
447 */
448
449 // make UNC path by concati'ing input pars in an intelligent way
FindPathStr(int drive,const unicode_t * s,const wchar_t * cat)450 static std::vector<wchar_t> FindPathStr( int drive, const unicode_t* s, const wchar_t* cat )
451 {
452 int lcat = Utf16Chars( cat );
453
454 std::vector<wchar_t> p( 10 + unicode_strlen( s ) + lcat + 1 );
455 wchar_t* d = p.data();
456
457 d[0] = '\\';
458 d[1] = '\\';
459 d[2] = '?';
460 d[3] = '\\';
461 d += 4;
462
463 if ( drive == -1 ) //???
464 {
465 d[0] = 'U';
466 d[1] = 'N';
467 d[2] = 'C';
468 //d[3]='\\'; //еше \ добавится из пути
469 d += 3;
470 }
471 else
472 {
473 d[0] = drive + 'a';
474 d[1] = ':';
475 d += 2;
476 }
477
478
479 unicode_t lastChar = 0;
480
481 for ( ; *s; s++, d++ ) { lastChar = *d = *s; }
482
483 // ensure that we do not append double-backslash to the filepath.
484 // FindFirstFileW does not like UNC like \\?\c:\\*, and prefers \\?\c:\*
485 if ( lastChar == '\\' && *cat == '\\' ) { cat++; }
486
487 for ( ; *cat; cat++, d++ ) { *d = *cat; }
488
489 *d = 0;
490
491 return p;
492 }
493
494 #ifdef _DEBUG
toStr(char * str,const wchar_t * wstr)495 static void toStr( char* str, const wchar_t* wstr )
496 {
497 while ( *wstr )
498 {
499 *str++ = char( ( *wstr++ ) & 0xFF );
500 }
501
502 *str = 0;
503 }
504 #endif
505
ReadDir(FSList * list,FSPath & _path,int * err,FSCInfo * info)506 int FSSys::ReadDir( FSList* list, FSPath& _path, int* err, FSCInfo* info )
507 {
508 list->Clear();
509 FSPath path( _path );
510 WIN32_FIND_DATAW ent;
511
512 HANDLE handle = FindFirstFileW( FindPathStr( _drive, path.GetUnicode(), L"\\*" ).data(), &ent );
513
514 #if defined( _DEBUG ) & 0
515 std::vector<wchar_t> wpath = FindPathStr( _drive, path.GetUnicode(), L"\\*" );
516 char s[1024];
517 toStr( s, wpath.data() );
518 dbg_printf( "FSSys::ReadDir %s@UNC path=%s\n", handle == INVALID_HANDLE_VALUE ? "OK" : "failed", s );
519 #endif
520
521 if ( handle == INVALID_HANDLE_VALUE )
522 {
523 DWORD ret = GetLastError();
524
525 if ( ret == ERROR_FILE_NOT_FOUND ) { return 0; }
526
527 SetError( err, GetLastError() );
528 return -1;
529 }
530
531 try
532 {
533 while ( true )
534 {
535 if ( info && info->IsStopped() )
536 {
537 FindClose( handle );
538 return -2;
539 }
540
541 //skip . and ..
542 if ( !( ent.cFileName[0] == '.' && ( !ent.cFileName[1] || ( ent.cFileName[1] == '.' && !ent.cFileName[2] ) ) ) )
543 {
544 clPtr<FSNode> pNode = new FSNode();
545 pNode->name.Set( CS_UNICODE, Utf16ToUnicode( ent.cFileName ).data() );
546
547 pNode->st.dwFileAttributes = ent.dwFileAttributes;
548 pNode->st.size = ( seek_t( ent.nFileSizeHigh ) << 32 ) + ent.nFileSizeLow;
549
550 pNode->st.m_CreationTime = ent.ftCreationTime;
551 pNode->st.m_LastAccessTime = ent.ftLastAccessTime;
552 pNode->st.m_LastWriteTime = ent.ftLastWriteTime;
553 // TODO: use ZwQueryDirectoryFile() to get the change time
554 pNode->st.m_ChangeTime = ent.ftLastWriteTime;
555
556 if ( ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
557 {
558 pNode->st.mode = S_IFDIR;
559 }
560 else
561 {
562 pNode->st.mode = S_IFREG;
563 }
564
565 pNode->st.mode |= 0664;
566 list->Append( pNode );
567 }
568
569 if ( !FindNextFileW( handle, &ent ) )
570 {
571 if ( GetLastError() == ERROR_NO_MORE_FILES ) { break; }
572
573 SetError( err, GetLastError() );
574 FindClose( handle );
575 return -1;
576 }
577 };
578
579 FindClose( handle );
580
581 return 0;
582 }
583 catch ( ... )
584 {
585 FindClose( handle );
586 throw;
587 }
588
589 SetError( err, 100 );
590 return -1;
591 }
592
Stat(FSPath & path,FSStat * fsStat,int * err,FSCInfo * info)593 int FSSys::Stat( FSPath& path, FSStat* fsStat, int* err, FSCInfo* info )
594 {
595 if ( ( _drive >= 0 && path.Count() == 1 ) || ( _drive == -1 && path.Count() == 3 ) )
596 {
597 //pseudo stat
598 fsStat->size = 0;
599 fsStat->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
600 fsStat->mode = S_IFDIR;
601 fsStat->m_CreationTime = 0;
602 fsStat->m_LastAccessTime = 0;
603 fsStat->m_LastWriteTime = 0;
604 fsStat->m_ChangeTime = 0;
605 fsStat->mode |= 0664;
606 return 0;
607 }
608
609 WIN32_FIND_DATAW ent;
610 HANDLE handle = FindFirstFileW( SysPathStr( _drive, path.GetUnicode() ).data(), &ent );
611
612 if ( handle == INVALID_HANDLE_VALUE )
613 {
614 SetError( err, GetLastError() );
615 return -1;
616 }
617
618 try
619 {
620 fsStat->size = ( seek_t( ent.nFileSizeHigh ) << 32 ) + ent.nFileSizeLow;
621 fsStat->dwFileAttributes = ent.dwFileAttributes;
622 fsStat->m_CreationTime = ent.ftCreationTime;
623 fsStat->m_LastAccessTime = ent.ftLastAccessTime;
624 fsStat->m_LastWriteTime = ent.ftLastWriteTime;
625 // TODO: use ZwQueryDirectoryFile() to get the change time
626 fsStat->m_ChangeTime = ent.ftLastWriteTime;
627
628 if ( ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
629 {
630 fsStat->mode = S_IFDIR;
631 }
632 else
633 {
634 fsStat->mode = S_IFREG;
635 }
636
637 fsStat->mode |= 0664;
638 FindClose( handle );
639 return 0;
640 }
641 catch ( ... )
642 {
643 FindClose( handle );
644 throw;
645 }
646
647 //...
648 SetError( err, 50 );
649 return -1;
650 }
651
StatSetAttr(FSPath & path,const FSStat * st,int * err,FSCInfo * info)652 int FSSys::StatSetAttr( FSPath& path, const FSStat* st, int* err, FSCInfo* info )
653 {
654 const unicode_t* lpFileName = path.GetUnicode();
655
656 DWORD Attr = st->dwFileAttributes;
657
658 return SetFileAttributesW( lpFileName, Attr ) ? 0 : -1;
659 }
660
GetFileSystemFreeSpace(FSPath & path,int * err)661 int64_t FSSys::GetFileSystemFreeSpace( FSPath& path, int* err )
662 {
663 DWORD SectorsPerCluster;
664 DWORD BytesPerSector;
665 DWORD NumberOfFreeClusters;
666 DWORD TotalNumberOfClusters;
667
668 int d = Drive();
669
670 char RootPath[] = { char( d + 'A' ), ':', '\\', 0 };
671
672 if ( GetDiskFreeSpace( RootPath, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters ) != TRUE ) { return -1; }
673
674 return ( int64_t )SectorsPerCluster * ( int64_t )BytesPerSector * ( int64_t )NumberOfFreeClusters;
675 }
676
FStat(int fd,FSStat * fsStat,int * err,FSCInfo * info)677 int FSSys::FStat( int fd, FSStat* fsStat, int* err, FSCInfo* info )
678 {
679 BY_HANDLE_FILE_INFORMATION e;
680 HANDLE* p = this->handles.Handle( fd );
681
682 if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
683
684
685 if ( !GetFileInformationByHandle( *p, &e ) )
686 {
687 SetError( err, GetLastError() );
688 return -1;
689 }
690
691 fsStat->size = ( seek_t( e.nFileSizeHigh ) << 32 ) + e.nFileSizeLow;
692 fsStat->dwFileAttributes = e.dwFileAttributes;
693 fsStat->m_CreationTime = e.ftCreationTime;
694 fsStat->m_LastAccessTime = e.ftLastAccessTime;
695 fsStat->m_LastWriteTime = e.ftLastWriteTime;
696 // TODO: use ZwQueryDirectoryFile() to get the change time
697 fsStat->m_ChangeTime = e.ftLastWriteTime;
698
699 if ( e.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
700 {
701 fsStat->mode = S_IFDIR;
702 }
703 else
704 {
705 fsStat->mode = S_IFREG;
706 }
707
708 fsStat->mode |= 0664;
709 return 0;
710 }
711
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)712 int FSSys::Symlink( FSPath& path, FSString& str, int* err, FSCInfo* info )
713 {
714 //...
715 SetError( err, 50 );
716 return -1;
717 }
718
StatVfs(FSPath & path,FSStatVfs * vst,int * err,FSCInfo * info)719 int FSSys::StatVfs( FSPath& path, FSStatVfs* vst, int* err, FSCInfo* info )
720 {
721 ccollect<wchar_t, 0x100> root;
722
723 root.append( '\\' );
724 root.append( '\\' );
725 root.append( '?' );
726 root.append( '\\' );
727
728 if ( Drive() == -1 )
729 {
730 root.append( 'U' );
731 root.append( 'N' );
732 root.append( 'C' );
733 root.append( '\\' );
734 int n = path.Count() < 3 ? path.Count() : 3;
735
736 for ( int i = 1; i < n; i++ )
737 {
738 const unicode_t* s = path.GetItem( i )->GetUnicode();
739
740 for ( ; *s; s++ ) { root.append( *s ); }
741
742 root.append( '\\' );
743 }
744 }
745 else
746 {
747 root.append( Drive() + 'a' );
748 root.append( ':' );
749 root.append( '\\' );
750 }
751
752 root.append( 0 );
753
754 DWORD SectorsPerCluster;
755 DWORD BytesPerSector;
756 DWORD NumberOfFreeClusters;
757 DWORD TotalNumberOfClusters;
758
759 if ( !GetDiskFreeSpaceW( root.ptr(), &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters ) )
760 {
761 SetError( err, GetLastError() );
762 return -1;
763 }
764
765 vst->size = int64_t( TotalNumberOfClusters ) * SectorsPerCluster * BytesPerSector;
766 vst->avail = int64_t( NumberOfFreeClusters ) * SectorsPerCluster * BytesPerSector;
767 return 0;
768 }
769
Uri(FSPath & path)770 FSString FSSys::Uri( FSPath& path )
771 {
772 unicode_t pref[0x100];
773 unicode_t* p = pref;
774
775 if ( _drive > 0 && _drive < 'z' - 'a' + 1 ) //+1 ???
776 {
777 *( p++ ) = 'A' + _drive;
778 *( p++ ) = ':';
779 }
780 else if ( _drive == -1 )
781 {
782 *( p++ ) = '\\';
783 }
784 else
785 {
786 *( p++ ) = '?';
787 *( p++ ) = ':';
788 }
789
790 *p = 0;
791
792 return FSString( carray_cat<unicode_t>( pref, path.GetUnicode() ).data() );
793 }
794
795 static std::unordered_map<int, std::vector<unicode_t> > userList;
796 static std::unordered_map<int, std::vector<unicode_t> > groupList;
797
798 static unicode_t c0 = 0;
799
GetOSUserName(int id)800 static unicode_t* GetOSUserName( int id )
801 {
802 return &c0;
803 }
804
GetOSGroupName(int id)805 static unicode_t* GetOSGroupName( int id )
806 {
807 return &c0;
808 }
809
GetUserName(int user,unicode_t buf[64])810 unicode_t* FSSys::GetUserName( int user, unicode_t buf[64] )
811 {
812 unicode_t* u = GetOSUserName( user );
813 unicode_t* s = buf;
814
815 for ( int n = 63; n > 0 && *u; n--, u++, s++ )
816 {
817 *s = *u;
818 }
819
820 *s = 0;
821 return buf;
822 };
823
824
GetGroupName(int group,unicode_t buf[64])825 unicode_t* FSSys::GetGroupName( int group, unicode_t buf[64] )
826 {
827 unicode_t* g = GetOSGroupName( group );
828 unicode_t* s = buf;
829
830 for ( int n = 63; n > 0 && *g; n--, g++, s++ )
831 {
832 *s = *g;
833 }
834
835 *s = 0;
836
837 return buf;
838 };
839
840 ////////////////////////// W32NetRes /////////////////////////
841
Set(NETRESOURCEW * p)842 void W32NetRes::Set( NETRESOURCEW* p )
843 {
844 Clear();
845
846 if ( !p ) { return; }
847
848 int nameLen = p->lpLocalName ? wcslen( p->lpLocalName ) + 1 : 0;
849 int remoteNameLen = p->lpRemoteName ? wcslen( p->lpRemoteName ) + 1 : 0;
850 int commentLen = p->lpComment ? wcslen( p->lpComment ) + 1 : 0;
851 int providerLen = p->lpProvider ? wcslen( p->lpProvider ) + 1 : 0;
852 int size = sizeof( Node ) + ( nameLen + remoteNameLen + commentLen + providerLen ) * sizeof( wchar_t );
853 data = new unsigned char[size];
854 Node& node = *( ( Node* )data );
855 node.size = size;
856 node.rs = *p;
857 int offset = sizeof( node );
858 #if _MSC_VER > 1700
859 # define QQQ(a, b, len) if (len>0) { a = (wchar_t*)(data + offset); Lwcsncpy(a, len, b, _TRUNCATE); } offset += len*sizeof(wchar_t);
860 #else
861 # define QQQ(a, b, len) if (len>0) { a = (wchar_t*)(data + offset); Lwcsncpy(a, b, len); } offset += len*sizeof(wchar_t);
862 #endif
863 QQQ( node.rs.lpLocalName, p->lpLocalName, nameLen );
864 QQQ( node.rs.lpRemoteName, p->lpRemoteName, remoteNameLen );
865 QQQ( node.rs.lpComment, p->lpComment, commentLen );
866 QQQ( node.rs.lpProvider, p->lpProvider, providerLen );
867 #undef QQQ
868 }
869
Copy(const unsigned char * a)870 unsigned char* W32NetRes::Copy( const unsigned char* a )
871 {
872 if ( !a ) { return 0; }
873
874 Node& src = *( ( Node* )a );
875
876 unsigned char* b = new unsigned char [src.size];
877 memcpy( b, a, src.size );
878
879 Node& dest = *( ( Node* )b );
880 #define QQQ(x) if (dest.rs.x) { dest.rs.x = (wchar_t*)(( ((char*)src.rs.x)-((char*)a)) + (char*)b); }
881 QQQ( lpLocalName );
882 QQQ( lpRemoteName );
883 QQQ( lpComment );
884 QQQ( lpProvider );
885 #undef QQQ
886 return b;
887 };
888
889
890 /////////////////////////// FSWin32Net /////////////////////////
891
Flags()892 unsigned FSWin32Net::Flags() { return 0; }
IsEEXIST(int err)893 bool FSWin32Net::IsEEXIST( int err ) { return false; };
IsENOENT(int err)894 bool FSWin32Net::IsENOENT( int err ) { return false; };
IsEXDEV(int err)895 bool FSWin32Net::IsEXDEV( int err ) { return false; };
896
StrError(int err)897 FSString FSWin32Net::StrError( int err )
898 {
899 if ( err == ERRNOSUPPORT )
900 {
901 return FSString( "Operation not supported by net fs" );
902 }
903
904 FSString ret;
905 sys_char_t buf[1024];
906 ret.SetSys( sys_error_str( err, buf, 0x100 ) );
907
908 return ret;
909 }
910
911
Equal(FS * fs)912 bool FSWin32Net::Equal( FS* fs ) { return false; };
913
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)914 int FSWin32Net::OpenRead ( FSPath& path, int flags, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)915 int FSWin32Net::OpenCreate ( FSPath& path, bool overwrite, int mode, int flags, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Close(int fd,int * err,FSCInfo * info)916 int FSWin32Net::Close ( int fd, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Read(int fd,void * buf,int size,int * err,FSCInfo * info)917 int FSWin32Net::Read ( int fd, void* buf, int size, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Write(int fd,void * buf,int size,int * err,FSCInfo * info)918 int FSWin32Net::Write ( int fd, void* buf, int size, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)919 int FSWin32Net::Seek( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)920 int FSWin32Net::Rename ( FSPath& oldpath, FSPath& newpath, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)921 int FSWin32Net::MkDir ( FSPath& path, int mode, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Delete(FSPath & path,int * err,FSCInfo * info)922 int FSWin32Net::Delete ( FSPath& path, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
RmDir(FSPath & path,int * err,FSCInfo * info)923 int FSWin32Net::RmDir ( FSPath& path, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
924
925 class WNetEnumerator
926 {
927 enum { BUFSIZE = 16 * 1024 };
928 HANDLE handle;
929 std::vector<char> buf;
930 int pos, count;
931 bool Fill( DWORD* pErr );
932 public:
WNetEnumerator()933 WNetEnumerator(): handle( 0 ), buf( BUFSIZE ), pos( 0 ), count( 0 ) {}
934
Close()935 void Close() { if ( handle ) { WNetCloseEnum( handle ); handle = 0; pos = count = 0; } }
Open(NETRESOURCEW * p)936 bool Open( NETRESOURCEW* p ) { return WNetOpenEnumW( RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, p, &handle ) == NO_ERROR; }
937
Next(DWORD * pErr)938 NETRESOURCEW* Next( DWORD* pErr )
939 {
940
941 if ( !Fill( pErr ) ) { return 0; }
942
943 if ( pErr ) { *pErr = 0; }
944
945 if ( count < 0 ) { return 0; }
946
947 NETRESOURCEW* p = ( ( NETRESOURCEW* )buf.data() ) + pos;
948 pos++;
949 return p;
950 }
951
~WNetEnumerator()952 ~WNetEnumerator() { Close(); }
953 };
954
Fill(DWORD * pErr)955 bool WNetEnumerator::Fill( DWORD* pErr )
956 {
957 if ( count < 0 || pos < count ) { return true; }
958
959 DWORD n = -1;
960 DWORD bSize = BUFSIZE;
961 DWORD res = WNetEnumResourceW( handle, &n, buf.data(), &bSize );
962
963 if ( res == ERROR_NO_MORE_ITEMS )
964 {
965 count = -1;
966 return true;
967 }
968
969 if ( res != NO_ERROR )
970 {
971 count = -1;
972
973 if ( pErr ) { *pErr = GetLastError(); }
974
975 return false;
976 }
977
978 count = n;
979 pos = 0;
980 return true;
981 }
982
983
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)984 int FSWin32Net::SetFileTime( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info )
985 {
986 if ( err ) { *err = ERRNOSUPPORT; } return -1;
987 }
988
ReadDir(FSList * list,FSPath & path,int * err,FSCInfo * info)989 int FSWin32Net::ReadDir ( FSList* list, FSPath& path, int* err, FSCInfo* info )
990 {
991 list->Clear();
992
993 if ( path.Count() > 1 )
994 {
995 if ( err ) { *err = ERRNOSUPPORT; }
996
997 return -1;
998 }
999
1000
1001 WNetEnumerator en;
1002
1003 if ( !en.Open( _res.Get() ) )
1004 {
1005 SetError( err, GetLastError() );
1006 return -1;
1007 }
1008
1009 try
1010 {
1011 while ( true )
1012 {
1013 if ( info && info->IsStopped() )
1014 {
1015 return -2;
1016 }
1017
1018 DWORD dwErr = 0;
1019 NETRESOURCEW* p = en.Next( &dwErr );
1020
1021 if ( !p )
1022 {
1023 if ( !dwErr ) { break; }
1024
1025 SetError( err, dwErr );
1026 return -1;
1027 }
1028
1029 if ( !p->lpRemoteName ) { continue; }
1030
1031 wchar_t* pName = p->lpRemoteName;
1032
1033 if ( p->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE || p->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER )
1034 {
1035 //выкинуть из названия шары имя сервера, а из названия сервера - косые символы
1036 wchar_t* last = 0;
1037
1038 for ( wchar_t* s = pName; *s; s++ )
1039 if ( *s == '\\' ) { last = s; }
1040
1041 if ( last && last[1] ) { pName = last + 1; }
1042 }
1043
1044 clPtr<FSNode> pNode = new FSNode();
1045
1046 pNode->name.Set( CS_UNICODE, Utf16ToUnicode( pName ).data() );
1047 pNode->st.mode = S_IFDIR;
1048 pNode->st.mode |= 0664;
1049
1050 pNode->_w32NetRes = W32NetRes( p );
1051
1052 switch ( p->dwDisplayType )
1053 {
1054 //case RESOURCEDISPLAYTYPE_GENERIC: return "GENERIC";
1055 case RESOURCEDISPLAYTYPE_DOMAIN:
1056 case RESOURCEDISPLAYTYPE_GROUP:
1057 case RESOURCEDISPLAYTYPE_NETWORK:
1058 pNode->extType = FSNode::WORKGROUP;
1059 break;
1060
1061 case RESOURCEDISPLAYTYPE_SERVER:
1062 pNode->extType = FSNode::SERVER;
1063 break;
1064
1065 case RESOURCEDISPLAYTYPE_DIRECTORY:
1066 case RESOURCEDISPLAYTYPE_SHARE:
1067 pNode->extType = FSNode::FILESHARE;
1068 break;
1069 };
1070
1071 list->Append( pNode );
1072 };
1073
1074 return 0;
1075 }
1076 catch ( ... )
1077 {
1078 throw;
1079 }
1080
1081 SetError( err, 100 );
1082 return -1;
1083 }
1084
Stat(FSPath & path,FSStat * st,int * err,FSCInfo * info)1085 int FSWin32Net::Stat ( FSPath& path, FSStat* st, int* err, FSCInfo* info ) { if ( err ) { *err = ERRNOSUPPORT; } return -1; }
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)1086 int FSWin32Net::Symlink ( FSPath& path, FSString& str, int* err, FSCInfo* info ) { if ( err ) { *err = ERRNOSUPPORT; } return -1; }
1087
Uri(FSPath & path)1088 FSString FSWin32Net::Uri( FSPath& path )
1089 {
1090 NETRESOURCEW* p = _res.Get();
1091
1092 if ( p && p->lpRemoteName )
1093 {
1094 return FSString( Utf16ToUnicode( p->lpRemoteName ).data() );
1095 }
1096
1097 return FSString( "Network" );
1098 }
1099
~FSWin32Net()1100 FSWin32Net::~FSWin32Net()
1101 {
1102 }
1103
1104 #else
1105
1106 #include <pwd.h>
1107 #include <grp.h>
1108
1109 #include <sys/types.h>
1110 #include <dirent.h>
1111 #include <sys/time.h>
1112
1113 // for statfs()
1114 #ifdef __linux__
1115 # include <sys/statfs.h>
1116 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
1117 # include <sys/param.h>
1118 # include <sys/mount.h>
1119 #endif
1120
1121 #ifdef __linux__
1122 # define OPENFLAG_LARGEFILE (O_LARGEFILE)
1123 #else
1124 # define OPENFLAG_LARGEFILE (0)
1125 #endif
1126
Flags()1127 unsigned FSSys::Flags() { return HAVE_READ | HAVE_WRITE | HAVE_SYMLINK | HAVE_SEEK; }
IsEEXIST(int err)1128 bool FSSys::IsEEXIST( int err ) { return err == EEXIST; }
IsENOENT(int err)1129 bool FSSys::IsENOENT( int err ) { return err == ENOENT; }
IsEXDEV(int err)1130 bool FSSys::IsEXDEV( int err ) { return err == EXDEV; }
1131
StrError(int err)1132 FSString FSSys::StrError( int err )
1133 {
1134 sys_char_t buf[1024] = "";
1135 FSString ret( sys_charset_id, ( char* )sys_error_str( err, buf, sizeof( buf ) ) );
1136 return ret;
1137 }
1138
Equal(FS * fs)1139 bool FSSys::Equal( FS* fs )
1140 {
1141 if ( !fs || fs->Type() != FS::SYSTEM ) { return false; }
1142
1143 return true;
1144 }
1145
1146
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)1147 int FSSys::OpenRead ( FSPath& path, int flags, int* err, FSCInfo* info )
1148 {
1149 int n = open( ( char* ) path.GetString( sys_charset_id, '/' ),
1150 O_RDONLY | OPENFLAG_LARGEFILE, 0 );
1151
1152 if ( n < 0 ) { SetError( err, errno ); return -1; }
1153
1154 return n;
1155 }
1156
1157
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)1158 int FSSys::OpenCreate ( FSPath& path, bool overwrite, int mode, int flags, int* err, FSCInfo* info )
1159 {
1160 int n = open( ( char* ) path.GetString( sys_charset_id, '/' ),
1161 O_CREAT | O_WRONLY | O_TRUNC | OPENFLAG_LARGEFILE | ( overwrite ? 0 : O_EXCL ) , mode );
1162
1163 if ( n < 0 ) { SetError( err, errno ); return -1; }
1164
1165 return n;
1166 }
1167
Close(int fd,int * err,FSCInfo * info)1168 int FSSys::Close( int fd, int* err, FSCInfo* info )
1169 {
1170 if ( close( fd ) )
1171 {
1172 SetError( err, errno );
1173 return -1;
1174 }
1175
1176 return 0;
1177 }
1178
Read(int fd,void * buf,int size,int * err,FSCInfo * info)1179 int FSSys::Read( int fd, void* buf, int size, int* err, FSCInfo* info )
1180 {
1181 int n = read( fd, buf, size );
1182
1183 if ( n < 0 ) { SetError( err, errno ); return -1; }
1184
1185 return n;
1186 }
1187
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)1188 int FSSys::Seek( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet, int* err, FSCInfo* info )
1189 {
1190
1191 seek_t n = lseek( fd, pos, mode );
1192
1193 if ( n < 0 ) { SetError( err, errno ); return -1; }
1194
1195 if ( pRet ) { *pRet = n; }
1196
1197 return 0;
1198 }
1199
1200
Write(int fd,void * buf,int size,int * err,FSCInfo * info)1201 int FSSys::Write( int fd, void* buf, int size, int* err, FSCInfo* info )
1202 {
1203 int n = write( fd, buf, size );
1204
1205 if ( n < 0 ) { SetError( err, errno ); return -1; }
1206
1207 return n;
1208 }
1209
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)1210 int FSSys::Rename ( FSPath& oldpath, FSPath& newpath, int* err, FSCInfo* info )
1211 {
1212 if ( rename( ( char* ) oldpath.GetString( sys_charset_id, '/' ), ( char* ) newpath.GetString( sys_charset_id, '/' ) ) )
1213 {
1214 SetError( err, errno );
1215 return -1;
1216 }
1217
1218 return 0;
1219 }
1220
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)1221 int FSSys::MkDir( FSPath& path, int mode, int* err, FSCInfo* info )
1222 {
1223 if ( mkdir( ( char* ) path.GetString( sys_charset_id, '/' ), mode ) )
1224 {
1225 SetError( err, errno );
1226 return -1;
1227 }
1228
1229 return 0;
1230
1231 }
1232
Delete(FSPath & path,int * err,FSCInfo * info)1233 int FSSys::Delete( FSPath& path, int* err, FSCInfo* info )
1234 {
1235 if ( unlink( ( char* ) path.GetString( sys_charset_id, '/' ) ) )
1236 {
1237 SetError( err, errno );
1238 return -1;
1239 }
1240
1241 return 0;
1242 }
1243
RmDir(FSPath & path,int * err,FSCInfo * info)1244 int FSSys::RmDir( FSPath& path, int* err, FSCInfo* info )
1245 {
1246 if ( rmdir( ( char* ) path.GetString( sys_charset_id, '/' ) ) )
1247 {
1248 SetError( err, errno );
1249 return -1;
1250 }
1251
1252 return 0;
1253 }
1254
1255
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)1256 int FSSys::SetFileTime ( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info )
1257 {
1258 struct timeval tv[2];
1259 tv[0].tv_sec = aTime;
1260 tv[0].tv_usec = 0;
1261 tv[1].tv_sec = mTime;
1262 tv[1].tv_usec = 0;
1263
1264 if ( utimes( ( char* ) path.GetString( sys_charset_id, '/' ), tv ) )
1265 {
1266 SetError( err, errno );
1267 return -1;
1268 }
1269
1270 return 0;
1271
1272 }
1273
ReadDir(FSList * list,FSPath & _path,int * err,FSCInfo * info)1274 int FSSys::ReadDir( FSList* list, FSPath& _path, int* err, FSCInfo* info )
1275 {
1276 list->Clear();
1277
1278 FSPath path( _path );
1279
1280 DIR* d = opendir( ( char* )path.GetString( sys_charset_id ) );
1281
1282 if ( !d )
1283 {
1284 SetError( err, errno );
1285 return -1;
1286 }
1287
1288 try
1289 {
1290 struct dirent ent, *pEnt;
1291
1292 int n = path.Count();
1293
1294 while ( true )
1295 {
1296 if ( info && info->IsStopped() )
1297 {
1298 closedir( d );
1299 return -2;
1300 }
1301
1302 if ( readdir_r( d, &ent, &pEnt ) )
1303 {
1304 SetError( err, errno );
1305 closedir( d );
1306 return -1;
1307 }
1308
1309 if ( !pEnt ) { break; }
1310
1311 //skip . and ..
1312 if ( ent.d_name[0] == '.' && ( !ent.d_name[1] || ( ent.d_name[1] == '.' && !ent.d_name[2] ) ) )
1313 {
1314 continue;
1315 }
1316
1317 clPtr<FSNode> pNode = new FSNode();
1318 path.SetItem( n, sys_charset_id, ent.d_name );
1319 Stat( path, &pNode->st, 0, info );
1320 #if defined(__APPLE__)
1321 if ( sys_charset_id == CS_UTF8 )
1322 {
1323 std::string normname = normalize_utf8_NFC( ent.d_name );
1324 pNode->name.Set( sys_charset_id, normname.data() );
1325 }
1326 else
1327 {
1328 pNode->name.Set( sys_charset_id, ent.d_name );
1329 }
1330 #else
1331 pNode->name.Set( sys_charset_id, ent.d_name );
1332 #endif
1333 list->Append( pNode );
1334 };
1335
1336 closedir( d );
1337
1338 return 0;
1339 }
1340 catch ( ... )
1341 {
1342 closedir( d );
1343 throw;
1344 }
1345 }
1346
GetFileSystemFreeSpace(FSPath & path,int * err)1347 int64_t FSSys::GetFileSystemFreeSpace( FSPath& path, int* err )
1348 {
1349 #if defined( __linux__ ) && !defined( __APPLE__ )
1350 struct statfs64 s;
1351
1352 if ( statfs64( path.GetUtf8(), &s ) == -1 )
1353 {
1354 SetError( err, errno );
1355 return -1;
1356 }
1357
1358 #else
1359 // FreeBSD and probably other systems have 64 bit support in regular statfs
1360 struct statfs s;
1361
1362 if ( statfs( path.GetUtf8(), &s ) == -1 )
1363 {
1364 SetError( err, errno );
1365 return -1;
1366 }
1367
1368 #endif
1369
1370 return ( int64_t )( s.f_bfree ) * ( int64_t )( s.f_bsize );
1371 }
1372
Stat(FSPath & path,FSStat * fsStat,int * err,FSCInfo * info)1373 int FSSys::Stat( FSPath& path, FSStat* fsStat, int* err, FSCInfo* info )
1374 {
1375 fsStat->link.Clear();
1376
1377 #ifdef S_IFLNK
1378 struct stat st_link;
1379
1380 if ( lstat( ( char* )path.GetString( sys_charset_id ), &st_link ) )
1381 {
1382 SetError( err, errno );
1383 return -1;
1384 };
1385
1386 if ( ( st_link.st_mode & S_IFMT ) == S_IFLNK )
1387 {
1388 char buf[1024];
1389 ssize_t ret = readlink( ( char* )path.GetString( sys_charset_id ), buf, sizeof( buf ) );
1390
1391 if ( ret >= sizeof( buf ) ) { ret = sizeof( buf ) - 1; }
1392
1393 if ( ret >= 0 ) { buf[ret] = 0; }
1394 else { buf[0] = 0; }
1395
1396 if ( ret >= 0 ) { fsStat->link.Set( sys_charset_id, buf ); }
1397 }
1398 else
1399 {
1400 fsStat->mode = st_link.st_mode;
1401 fsStat->size = st_link.st_size;
1402 fsStat->m_CreationTime = st_link.st_ctime;
1403 fsStat->m_LastAccessTime = st_link.st_atime;
1404 fsStat->m_LastWriteTime = st_link.st_mtime;
1405 fsStat->m_ChangeTime = st_link.st_mtime;
1406 fsStat->gid = st_link.st_gid;
1407 fsStat->uid = st_link.st_uid;
1408
1409 fsStat->dev = st_link.st_dev;
1410 fsStat->ino = st_link.st_ino;
1411
1412 return 0;
1413 }
1414 #endif
1415
1416 struct stat st;
1417
1418 if ( stat( ( char* )path.GetString( sys_charset_id ), &st ) )
1419 {
1420 SetError( err, errno );
1421 return -1;
1422 }
1423
1424 fsStat->mode = st.st_mode;
1425 fsStat->size = st.st_size;
1426 fsStat->m_CreationTime = st.st_ctime;
1427 fsStat->m_LastAccessTime = st.st_atime;
1428 fsStat->m_LastWriteTime = st.st_mtime;
1429 fsStat->m_ChangeTime = st.st_mtime;
1430 fsStat->gid = st.st_gid;
1431 fsStat->uid = st.st_uid;
1432
1433 fsStat->dev = st.st_dev;
1434 fsStat->ino = st.st_ino;
1435
1436 return 0;
1437 }
1438
StatSetAttr(FSPath & path,const FSStat * st,int * err,FSCInfo * info)1439 int FSSys::StatSetAttr( FSPath& path, const FSStat* st, int* err, FSCInfo* info )
1440 {
1441 // TODO: Complete - at least, add a mc-style chown.
1442 int status = chmod( path.GetUtf8(), (mode_t) st->mode );
1443 if ( status != 0)
1444 {
1445 SetError( err, errno );
1446 }
1447 return status;
1448 }
1449
FStat(int fd,FSStat * fsStat,int * err,FSCInfo * info)1450 int FSSys::FStat( int fd, FSStat* fsStat, int* err, FSCInfo* info )
1451 {
1452 fsStat->link.Clear();
1453
1454 struct stat st;
1455
1456 if ( fstat( fd, &st ) )
1457 {
1458 SetError( err, errno );
1459 return -1;
1460 }
1461
1462 fsStat->mode = st.st_mode;
1463 fsStat->size = st.st_size;
1464 fsStat->m_CreationTime = st.st_ctime;
1465 fsStat->m_LastAccessTime = st.st_atime;
1466 fsStat->m_LastWriteTime = st.st_mtime;
1467 fsStat->m_ChangeTime = st.st_mtime;
1468 fsStat->gid = st.st_gid;
1469 fsStat->uid = st.st_uid;
1470
1471 fsStat->dev = st.st_dev;
1472 fsStat->ino = st.st_ino;
1473
1474 return 0;
1475 }
1476
1477
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)1478 int FSSys::Symlink( FSPath& path, FSString& str, int* err, FSCInfo* info )
1479 {
1480 if ( symlink( ( char* )str.Get( sys_charset_id ), ( char* )path.GetString( sys_charset_id ) ) )
1481 {
1482 SetError( err, errno );
1483 return -1;
1484 }
1485
1486 return 0;
1487 }
1488
1489 static std::unordered_map<int, std::vector<unicode_t> > userList;
1490 static std::unordered_map<int, std::vector<unicode_t> > groupList;
1491
1492 #include <sys/statvfs.h>
1493
StatVfs(FSPath & path,FSStatVfs * vst,int * err,FSCInfo * info)1494 int FSSys::StatVfs( FSPath& path, FSStatVfs* vst, int* err, FSCInfo* info )
1495 {
1496 struct statvfs st;
1497
1498 if ( statvfs( ( char* )path.GetString( sys_charset_id ), &st ) )
1499 {
1500 SetError( err, errno );
1501 return -1;
1502 }
1503
1504 vst->size = int64_t( st.f_blocks ) * st.f_frsize;
1505 vst->avail = int64_t( st.f_bavail ) * st.f_frsize;
1506
1507 return 0;
1508 }
1509
Uri(FSPath & path)1510 FSString FSSys::Uri( FSPath& path )
1511 {
1512 return FSString( path.GetUnicode() );
1513 }
1514
GetOSUserName(int id)1515 static std::vector<unicode_t> GetOSUserName( int id )
1516 {
1517 auto i = userList.find( id );
1518
1519 if ( i != userList.end() ) { return i->second; }
1520
1521 setpwent();
1522 struct passwd* p = getpwuid( id );
1523 char buf[64];
1524 char* nameStr = p ? p->pw_name : buf;
1525
1526 if ( !p ) { sprintf( buf, "%i", id ); }
1527
1528 std::vector<unicode_t> str = sys_to_unicode_array( nameStr );
1529 userList[id] = str;
1530 endpwent();
1531 return str;
1532 }
1533
GetOSGroupName(int id)1534 static std::vector<unicode_t> GetOSGroupName( int id )
1535 {
1536 auto i = groupList.find( id );
1537
1538 if ( i != groupList.end() ) { return i->second; }
1539
1540 setgrent();
1541 struct group* p = getgrgid( id );
1542 char buf[64];
1543 char* nameStr = p ? p->gr_name : buf;
1544
1545 if ( !p ) { sprintf( buf, "%i", id ); }
1546
1547 std::vector<unicode_t> str = sys_to_unicode_array( nameStr );
1548 groupList[id] = str;
1549 endgrent();
1550 return str;
1551 }
1552
GetUserName(int user,unicode_t buf[64])1553 unicode_t* FSSys::GetUserName( int user, unicode_t buf[64] )
1554 {
1555 std::vector<unicode_t> Name = GetOSUserName( user );
1556 unicode_t* u = Name.data();
1557 unicode_t* s = buf;
1558
1559 for ( int n = 63; n > 0 && *u; n--, u++, s++ )
1560 {
1561 *s = *u;
1562 }
1563
1564 *s = 0;
1565 return buf;
1566 };
1567
1568
GetGroupName(int group,unicode_t buf[64])1569 unicode_t* FSSys::GetGroupName( int group, unicode_t buf[64] )
1570 {
1571 std::vector<unicode_t> Name = GetOSGroupName( group );
1572
1573 unicode_t* g = Name.data();
1574 unicode_t* s = buf;
1575
1576 for ( int n = 63; n > 0 && *g; n--, g++, s++ )
1577 {
1578 *s = *g;
1579 }
1580
1581 *s = 0;
1582
1583 return buf;
1584 };
1585
1586 #endif
1587
~FSSys()1588 FSSys::~FSSys() {}
1589
1590
1591 //////////////////////////////////////// FSList /////////////////////////////////////
1592
Append(clPtr<FSNode> p)1593 void FSList::Append( clPtr<FSNode> p )
1594 {
1595 p->next = 0;
1596
1597 if ( last )
1598 {
1599 last->next = p.ptr();
1600 }
1601 else
1602 {
1603 first = p.ptr();
1604 }
1605
1606 last = p.ptr();
1607 p.drop();
1608 count++;
1609 }
1610
Clear()1611 void FSList::Clear()
1612 {
1613 for ( FSNode* p = first; p; )
1614 {
1615 FSNode* t = p;
1616 p = p->next;
1617 delete t;
1618 }
1619
1620 first = last = 0;
1621 count = 0;
1622 }
1623
CopyFrom(const FSList & a,bool onlySelected)1624 void FSList::CopyFrom( const FSList& a, bool onlySelected )
1625 {
1626 Clear();
1627
1628 for ( FSNode* p = a.first; p; p = p->next )
1629 {
1630 if ( onlySelected && !p->isSelected ) { continue; }
1631
1632 FSNode* node = new FSNode( *p );
1633
1634 if ( last )
1635 {
1636 last->next = node;
1637 }
1638 else
1639 {
1640 first = node;
1641 }
1642
1643 last = node;
1644 count++;
1645 }
1646 }
1647
CopyOne(FSNode * node)1648 void FSList::CopyOne( FSNode* node )
1649 {
1650 Clear();
1651 FSNode* p = new FSNode( *node );
1652 first = last = p;
1653 count = 1;
1654 }
1655
GetArray()1656 std::vector<FSNode*> FSList::GetArray()
1657 {
1658 std::vector<FSNode*> p( Count() );
1659 FSNode* pNode = first;
1660 int n = Count();
1661
1662 for ( int i = 0 ; i < n && pNode; i++, pNode = pNode->next )
1663 {
1664 p[i] = pNode;
1665 }
1666
1667 return p;
1668 }
1669
GetFilteredArray(bool showHidden,int * pCount)1670 std::vector<FSNode*> FSList::GetFilteredArray( bool showHidden, int* pCount )
1671 {
1672 if ( pCount ) { *pCount = 0; }
1673
1674 std::vector<FSNode*> p( Count() );
1675 FSNode* pNode = first;
1676 int n = Count();
1677 int i;
1678
1679 for ( i = 0 ; i < n && pNode; pNode = pNode->next )
1680 if ( showHidden || !pNode->IsHidden() )
1681 {
1682 p[i++] = pNode;
1683 }
1684
1685 if ( pCount ) { *pCount = i; }
1686
1687 return p;
1688 }
1689
1690
1691 // binary search.
1692 // Returns index of the found node
1693 // in EXACT_MATCH_ONLY mode returns -1 if node not found
1694 // in EXACT_OR_CLOSEST_PRECEDING_NODE modes returns -1 if n is less than the 1st nodeVector element
1695 // in EXACT_OR_CLOSEST_SUCCEEDING_NODE modes returns vsize if n is greater than the last nodeVector element
_BSearch(FSNode & n,const std::vector<FSNode * > & nodeVector,int (* CmpFunc)(FSNode * n1,FSNode * n2),BSearchMode bSearchMode)1696 static int _BSearch(FSNode& n, const std::vector<FSNode*>& nodeVector, int(*CmpFunc)(FSNode* n1, FSNode* n2), BSearchMode bSearchMode)
1697 {
1698 int vsize = nodeVector.size();
1699 if (vsize == 0)
1700 return -1;
1701
1702 int iLeft = 0;
1703 int iRight = vsize-1;
1704 //int cmp = CmpFunc(&n, nodeVector[iLeft]);
1705
1706 for (int i = iRight / 2; iLeft <= iRight ; i = (iLeft + iRight) / 2)
1707 {
1708 int cmp = CmpFunc(&n, nodeVector[i]);
1709 if (cmp == 0) // found exact match
1710 return i;
1711 if (cmp > 0)
1712 iLeft = i + 1;
1713 else
1714 iRight = i - 1;
1715 }
1716
1717 switch (bSearchMode)
1718 {
1719 case EXACT_OR_CLOSEST_PRECEDING_NODE:
1720 return iRight;
1721 case EXACT_OR_CLOSEST_SUCCEEDING_NODE:
1722 return iLeft;
1723 default:
1724 case EXACT_MATCH_ONLY:
1725 return -1;
1726 }
1727 }
1728
1729
1730 // standard: returns n1 - n2, for bsearch
1731 template <bool isAscending, bool isCaseSensitive, SORT_MODE mode>
CmpFunc(FSNode * a,FSNode * b)1732 int CmpFunc(FSNode* a, FSNode* b)
1733 {
1734 // directories always go first
1735 int dirCmp = a->IsDir() - b->IsDir();
1736 if (dirCmp)
1737 return -dirCmp;
1738 switch (mode)
1739 {
1740 case SORT_EXT:
1741 {
1742 int cmpExt = a->CmpByExt(*b, isCaseSensitive);
1743 if (cmpExt)
1744 return isAscending ? cmpExt : -cmpExt;
1745 } // if extensions match, do name comparison using current ascending mode
1746 // i.e. fall into the next case
1747 case SORT_NAME:
1748 {
1749 int cmpName = isCaseSensitive ? a->name.Cmp(b->name) : a->name.CmpNoCase(b->name);
1750 return isAscending ? cmpName : -cmpName;
1751 }
1752 case SORT_SIZE:
1753 {
1754 int64_t cmpSize = a->st.size - b->st.size;
1755 if (cmpSize)
1756 return isAscending ? (cmpSize > 0 ? 1 : -1) : (cmpSize > 0 ? -1 : 1);
1757 break;
1758 }
1759 case SORT_MTIME:
1760 {
1761 time_t cmpTime = a->st.m_LastWriteTime - b->st.m_LastWriteTime;
1762 if (cmpTime)
1763 return isAscending ? (cmpTime > 0 ? 1 : -1) : (cmpTime > 0 ? -1 : 1);
1764 break;
1765 }
1766 default:
1767 return 0;
1768 }
1769 // if size|mtime match, return name comparison in ascending=true mode
1770 return isCaseSensitive ? a->name.Cmp(b->name) : a->name.CmpNoCase(b->name);
1771 }
1772
getCmpFunc(bool isAscending,bool isCaseSensitive,SORT_MODE sortMode)1773 FSNodeCmpFunc* FSNodeVectorSorter::getCmpFunc(bool isAscending, bool isCaseSensitive, SORT_MODE sortMode)
1774 {
1775 switch (sortMode)
1776 {
1777 case SORT_NAME:
1778 return isAscending ?
1779 (isCaseSensitive ? CmpFunc<true, true, SORT_NAME> : CmpFunc<true, false, SORT_NAME>) :
1780 (isCaseSensitive ? CmpFunc<false, true, SORT_NAME> : CmpFunc<false, false, SORT_NAME>);
1781 case SORT_EXT:
1782 return isAscending ?
1783 (isCaseSensitive ? CmpFunc<true, true, SORT_EXT> : CmpFunc<true, false, SORT_EXT>) :
1784 (isCaseSensitive ? CmpFunc<false, true, SORT_EXT> : CmpFunc<false, false, SORT_EXT>);
1785 break;
1786 case SORT_SIZE:
1787 return isAscending ? CmpFunc<true, true, SORT_SIZE> : CmpFunc<false, true, SORT_SIZE>;
1788 break;
1789 case SORT_MTIME:
1790 return isAscending ? CmpFunc<true, true, SORT_MTIME> : CmpFunc<false, true, SORT_MTIME>;
1791 default: // SORT_NONE
1792 break;
1793 }
1794 return nullptr;
1795 }
1796
Sort(std::vector<FSNode * > & nodeVector,bool isAscending,bool isCaseSensitive,SORT_MODE sortMode)1797 void FSNodeVectorSorter::Sort(std::vector<FSNode*>& nodeVector,
1798 bool isAscending, bool isCaseSensitive, SORT_MODE sortMode)
1799 {
1800 struct GreaterCmp
1801 {
1802 FSNodeCmpFunc* pCmpFunc;
1803 GreaterCmp(FSNodeCmpFunc* _pCmpFunc) : pCmpFunc(_pCmpFunc){}
1804 bool operator() (FSNode* n1, FSNode* n2)
1805 {
1806 return pCmpFunc(n1, n2) < 0;
1807 }
1808 };
1809
1810 FSNodeCmpFunc* pFunc = getCmpFunc(isAscending, isCaseSensitive, sortMode);
1811 if (pFunc)
1812 {
1813 std::sort(nodeVector.begin(), nodeVector.end(), GreaterCmp(pFunc));
1814 }
1815 }
1816
BSearch(FSNode & n,const std::vector<FSNode * > & nodeVector,BSearchMode searchMode,bool isAscending,bool isCaseSensitive,SORT_MODE sortMode)1817 int FSNodeVectorSorter::BSearch(FSNode& n, const std::vector<FSNode*>& nodeVector,
1818 BSearchMode searchMode, bool isAscending, bool isCaseSensitive, SORT_MODE sortMode)
1819 {
1820 FSNodeCmpFunc *pFunc = getCmpFunc(isAscending, isCaseSensitive, sortMode);
1821 if (pFunc)
1822 return _BSearch(n, nodeVector, pFunc, searchMode);
1823 else
1824 return -1;
1825 }
1826
1827 ///////////////////////////////////// FSStat ////////////////////////////////////////
1828
1829
GetModeStr(unicode_t buf[64])1830 unicode_t* FSStat::GetModeStr( unicode_t buf[64] )
1831 {
1832 unicode_t* p = buf;
1833 /* print type */
1834
1835 if ( IsLnk() ) { *p++ = '>'; }
1836
1837 switch ( mode & S_IFMT )
1838 {
1839 case S_IFDIR: /* directory */
1840 *p++ = 'd';
1841 break;
1842
1843 case S_IFCHR: /* character special */
1844 *p++ = 'c';
1845 break;
1846
1847 case S_IFBLK: /* block special */
1848 *p++ = 'b';
1849 break;
1850
1851 case S_IFREG: /* regular */
1852 *p++ = '-';
1853 break;
1854 #ifdef S_IFLNK
1855
1856 case S_IFLNK: /* symbolic link */
1857 *p++ = 'l';
1858 break;
1859 #endif
1860 #ifdef S_IFSOCK
1861
1862 case S_IFSOCK: /* socket */
1863 *p++ = 's';
1864 break;
1865 #endif
1866 #ifdef S_IFIFO
1867
1868 case S_IFIFO: /* fifo */
1869 *p++ = 'p';
1870 break;
1871 #endif
1872
1873 default: /* unknown */
1874 *p++ = '?';
1875 break;
1876 }
1877
1878 /* usr */
1879 if ( mode & S_IRUSR )
1880 {
1881 *p++ = 'r';
1882 }
1883 else
1884 {
1885 *p++ = '-';
1886 }
1887
1888 if ( mode & S_IWUSR )
1889 {
1890 *p++ = 'w';
1891 }
1892 else
1893 {
1894 *p++ = '-';
1895 }
1896
1897 switch ( mode & ( S_IXUSR | S_ISUID ) )
1898 {
1899 case 0:
1900 *p++ = '-';
1901 break;
1902
1903 case S_IXUSR:
1904 *p++ = 'x';
1905 break;
1906
1907 case S_ISUID:
1908 *p++ = 'S';
1909 break;
1910
1911 case S_IXUSR | S_ISUID:
1912 *p++ = 's';
1913 break;
1914 }
1915
1916 /* group */
1917 if ( mode & S_IRGRP )
1918 {
1919 *p++ = 'r';
1920 }
1921 else
1922 {
1923 *p++ = '-';
1924 }
1925
1926 if ( mode & S_IWGRP )
1927 {
1928 *p++ = 'w';
1929 }
1930 else
1931 {
1932 *p++ = '-';
1933 }
1934
1935 switch ( mode & ( S_IXGRP | S_ISGID ) )
1936 {
1937 case 0:
1938 *p++ = '-';
1939 break;
1940
1941 case S_IXGRP:
1942 *p++ = 'x';
1943 break;
1944
1945 case S_ISGID:
1946 *p++ = 'S';
1947 break;
1948
1949 case S_IXGRP | S_ISGID:
1950 *p++ = 's';
1951 break;
1952 }
1953
1954 /* other */
1955 if ( mode & S_IROTH )
1956 {
1957 *p++ = 'r';
1958 }
1959 else
1960 {
1961 *p++ = '-';
1962 }
1963
1964 if ( mode & S_IWOTH )
1965 {
1966 *p++ = 'w';
1967 }
1968 else
1969 {
1970 *p++ = '-';
1971 }
1972
1973 switch ( mode & ( S_IXOTH | S_ISVTX ) )
1974 {
1975 case 0:
1976 *p++ = '-';
1977 break;
1978
1979 case S_IXOTH:
1980 *p++ = 'x';
1981 break;
1982
1983 case S_ISVTX:
1984 *p++ = 'T';
1985 break;
1986
1987 case S_IXOTH | S_ISVTX:
1988 *p++ = 't';
1989 break;
1990 }
1991
1992 *p++ = ' '; /* will be a '+' if ACL's implemented */
1993 *p = '\0';
1994 return buf;
1995 }
1996
GetFSTimeStrTime(FSTime TimeValue)1997 std::string GetFSTimeStrTime( FSTime TimeValue )
1998 {
1999 char str[64];
2000 #ifdef _WIN32
2001 FILETIME mt = TimeValue;
2002 FILETIME lt;
2003 SYSTEMTIME st;
2004
2005 if (!FileTimeToLocalFileTime(&mt, <) || !FileTimeToSystemTime(<, &st))
2006 {
2007 return std::string("?");
2008 }
2009
2010 Lsnprintf( str, sizeof(str), "%02i:%02i:%02i,%03i", int(st.wHour), int(st.wMinute), int(st.wSecond), int(st.wMilliseconds) );
2011 #else
2012 time_t mt = TimeValue;
2013 struct tm* p = localtime(&mt);
2014
2015 if (p)
2016 {
2017 sprintf( str, "%02i:%02i:%02i", p->tm_hour, p->tm_min, p->tm_sec );
2018 }
2019 else
2020 {
2021 sprintf( str, "%02i:%02i:%02i", int(0), int(0), int(0) );
2022 }
2023 #endif
2024 return std::string( str );
2025 }
2026
GetFSTimeStrDate(FSTime TimeValue)2027 std::string GetFSTimeStrDate( FSTime TimeValue )
2028 {
2029 char str[64];
2030 #ifdef _WIN32
2031 FILETIME mt = TimeValue;
2032 FILETIME lt;
2033 SYSTEMTIME st;
2034
2035 if ( !FileTimeToLocalFileTime(&mt, <) || !FileTimeToSystemTime(<, &st) )
2036 {
2037 return std::string("?");
2038 }
2039
2040 Lsnprintf(str, sizeof(str), "%02i.%02i.%04i", int( st.wDay ), int( st.wMonth ), int( st.wYear ) );
2041
2042 #else
2043 time_t mt = TimeValue;
2044 struct tm* p = localtime(&mt);
2045
2046 if ( p )
2047 {
2048 sprintf( str, "%02i.%02i.%04i", p->tm_mday, p->tm_mon + 1, p->tm_year + 1900 );
2049 }
2050 else
2051 {
2052 sprintf(str, "%02i.%02i.%04i", int(0), int(0), int(0) + 1900 );
2053
2054 }
2055 #endif
2056 return std::string(str);
2057 }
2058
GetFSTimeFromStr(const std::string & Date,const std::string & Time)2059 FSTime GetFSTimeFromStr( const std::string& Date, const std::string& Time )
2060 {
2061 if ( Date.empty() || Time.empty() ) return FSTime();
2062
2063 int Day = 0;
2064 int Month = 0;
2065 int Year = 0;
2066 int Hour = 0;
2067 int Minute = 0;
2068 int Second = 0;
2069 int Milliseconds = 0;
2070
2071 if ( Lsscanf( Date.c_str(), "%i.%i.%i", &Day, &Month, &Year ) != 3 ) return FSTime();
2072 int NumValues = Lsscanf( Time.c_str(), "%i:%i:%i,%i", &Hour, &Minute, &Second, &Milliseconds );
2073 if ( NumValues != 3 && NumValues != 4 ) return FSTime();
2074
2075 FSTime Result;
2076
2077 #if defined(_WIN32)
2078 SYSTEMTIME SystemTime;
2079 memset( &SystemTime, 0, sizeof(SystemTime) );
2080 SystemTime.wDay = Day;
2081 SystemTime.wMonth = Month;
2082 SystemTime.wYear = Year;
2083 SystemTime.wHour = Hour;
2084 SystemTime.wMinute = Minute;
2085 SystemTime.wSecond = Second;
2086 SystemTime.wMilliseconds = Milliseconds;
2087
2088 FILETIME LocalTime;
2089 FILETIME FileTime;
2090
2091 if ( !SystemTimeToFileTime( &SystemTime, &LocalTime ) ) return FSTime();
2092 if ( !LocalFileTimeToFileTime( &LocalTime, &FileTime ) ) return FSTime();
2093
2094 Result = FileTime;
2095 #else
2096 tm p;
2097 p.tm_mday = Day;
2098 p.tm_mon = Month - 1;
2099 p.tm_year = Year - 1900;
2100 p.tm_hour = Hour;
2101 p.tm_min = Minute;
2102 p.tm_sec = Second;
2103
2104 time_t mt = mktime( &p );
2105
2106 Result = mt;
2107 #endif
2108
2109 return Result;
2110 }
2111
FSTimeToStr(unicode_t ret[64],FSTime TimeValue)2112 unicode_t* FSTimeToStr( unicode_t ret[64], FSTime TimeValue )
2113 {
2114 char str[64];
2115 unicode_t* t = ret;
2116 #ifdef _WIN32
2117 FILETIME mt = TimeValue;
2118 FILETIME lt;
2119 SYSTEMTIME st;
2120
2121 if ( !FileTimeToLocalFileTime( &mt, < ) || !FileTimeToSystemTime( <, &st ) ) { ret[0] = '?'; ret[1] = 0; return ret; }
2122
2123 Lsnprintf( str, sizeof( str ), "%02i.%02i.%04i %02i:%02i:%02i",
2124 int( st.wDay ), int( st.wMonth ), int( st.wYear ),
2125 int( st.wHour ), int( st.wMinute ), int( st.wSecond ) );
2126 #else
2127 time_t mt = TimeValue;
2128 struct tm* p = localtime( &mt );
2129
2130 if ( p )
2131 {
2132 sprintf( str, "%02i.%02i.%04i %02i:%02i:%02i",
2133 p->tm_mday, p->tm_mon + 1, p->tm_year + 1900, // % 100,
2134 p->tm_hour, p->tm_min, p->tm_sec );
2135 }
2136 else
2137 {
2138 sprintf( str, "%02i.%02i.%04i %02i:%02i:%02i",
2139 int( 0 ), int( 0 ), int( 0 ) + 1900, // % 100,
2140 int( 0 ), int( 0 ), int( 0 ) );
2141
2142 }
2143
2144 #endif
2145
2146 for ( char* s = str; *s; s++, t++ )
2147 {
2148 *t = *s;
2149 }
2150
2151 *t = 0;
2152 return ret;
2153 }
2154
GetMTimeStr(unicode_t ret[64])2155 unicode_t* FSStat::GetMTimeStr( unicode_t ret[64] )
2156 {
2157 return FSTimeToStr( ret, m_LastWriteTime );
2158 }
2159
GetPrintableSizeStr(unicode_t buf[64])2160 unicode_t* FSStat::GetPrintableSizeStr( unicode_t buf[64] )
2161 {
2162 unicode_t str[10];
2163 str[0] = 0;
2164
2165 seek_t num = size;
2166
2167 if ( num >= seek_t( 10l ) * 1024 * 1024 * 1024 )
2168 {
2169 num /= seek_t( 1024l ) * 1024 * 1024;
2170 str[0] = ' ';
2171 str[1] = 'G';
2172 str[2] = 0;
2173 }
2174 else if ( num >= 10l * 1024 * 1024 )
2175 {
2176 num /= 1024 * 1024;
2177 str[0] = ' ';
2178 str[1] = 'M';
2179 str[2] = 0;
2180 }
2181 else if ( num >= 1024 * 1024 )
2182 {
2183 num /= 1024;
2184 str[0] = ' ';
2185 str[1] = 'K';
2186 str[2] = 0;
2187 }
2188
2189
2190 std::string Str = ToString( num );
2191
2192 unicode_t* us = buf;
2193
2194 for ( const char* s = Str.c_str(); *s; s++ )
2195 {
2196 *( us++ ) = *s;
2197 }
2198
2199 for ( unicode_t* t = str; *t; t++ )
2200 {
2201 *( us++ ) = *t;
2202 }
2203
2204 *us = 0;
2205
2206 return buf;
2207 }
2208
2209
2210 ///////////////////////////////////// FSNode ////////////////////////////////////////
2211
unicode_rchr(const unicode_t * s,int c)2212 inline const unicode_t* unicode_rchr( const unicode_t* s, int c )
2213 {
2214 const unicode_t* p = 0;
2215
2216 if ( s )
2217 for ( ; *s; s++ ) if ( *s == c ) { p = s; }
2218
2219 return p;
2220 }
2221
CmpNoCase(const unicode_t * a,const unicode_t * b)2222 inline int CmpNoCase( const unicode_t* a, const unicode_t* b )
2223 {
2224 unicode_t au = 0;
2225 unicode_t bu = 0;
2226
2227 for ( ; *a; a++, b++ )
2228 {
2229 au = UnicodeLC( *a );
2230 bu = UnicodeLC( *b );
2231
2232 if ( au != bu ) { break; }
2233 };
2234
2235 return ( *a ? ( *b ? ( au < bu ? -1 : ( au == bu ? 0 : 1 ) ) : 1 ) : ( *b ? -1 : 0 ) );
2236 }
2237
2238
2239 #ifdef _WIN32
2240
IsExe()2241 bool FSNode::IsExe()
2242 {
2243 const unicode_t* s = unicode_rchr( GetUnicodeName(), '.' );
2244
2245 if ( !s || !*s ) { return false; }
2246
2247 s++;
2248
2249 if ( UnicodeLC( s[0] ) == 'e' && UnicodeLC( s[1] ) == 'x' && UnicodeLC( s[2] ) == 'e' && !s[3] ) { return true; }
2250
2251 if ( UnicodeLC( s[0] ) == 'b' && UnicodeLC( s[1] ) == 'a' && UnicodeLC( s[2] ) == 't' && !s[3] ) { return true; }
2252
2253 if ( UnicodeLC( s[0] ) == 'c' && UnicodeLC( s[1] ) == 'm' && UnicodeLC( s[2] ) == 'd' && !s[3] ) { return true; }
2254
2255 return false;
2256 }
2257
2258 #endif
2259
2260
2261
CmpByExt(FSNode & a,bool case_sensitive)2262 int FSNode::CmpByExt( FSNode& a, bool case_sensitive )
2263 {
2264 const unicode_t* s1 = unicode_rchr( GetUnicodeName(), '.' );
2265 const unicode_t* s2 = unicode_rchr( a.GetUnicodeName(), '.' );
2266
2267 if ( s1 )
2268 return ( s2 ) ?
2269 ( case_sensitive ? CmpStr<const unicode_t>( s1, s2 ) : CmpNoCase( s1, s2 ) )
2270 : 1;
2271 else
2272 {
2273 return s2 ? -1 : 0;
2274 }
2275 }
2276
2277
2278
~FSNode()2279 FSNode::~FSNode() {}
2280
2281 //////////////////////////////////// Utils ///////////////////////////////////////////
2282
ParzeLink(FSPath & path,FSString & link)2283 bool ParzeLink( FSPath& path, FSString& link )
2284 {
2285 FSPath t( link );
2286
2287 if ( !path.IsAbsolute() && !t.IsAbsolute() ) { return false; } //не абсолютный путь
2288
2289 int first = 0;
2290
2291 if ( t.IsAbsolute() )
2292 {
2293 path.Clear();
2294 path.PushStr( FSString( "" ) );
2295 first = 1;
2296 }
2297
2298 for ( int i = first; i < t.Count(); i++ )
2299 {
2300 FSString p = *( t.GetItem( i ) );
2301
2302 if ( p.IsDot() ) { continue; }
2303
2304 if ( p.Is2Dot() )
2305 {
2306 if ( path.Count() > 1 ) { path.Pop(); }
2307 }
2308 else
2309 {
2310 path.PushStr( p );
2311 }
2312 }
2313
2314 return true;
2315 }
2316