1 /*
2 * Part of WCM Commander
3 * https://github.com/corporateshark/WCMCommander
4 * wcm@linderdaum.com
5 */
6
7 #include "vfs-smb.h"
8
9 #ifdef LIBSMBCLIENT_EXIST
10
11 #include "string-util.h"
12
13 #ifdef LIBSMB40
14 # include <samba-4.0/libsmbclient.h>
15 #else
16 # include <libsmbclient.h>
17 #endif
18
19 #include <sys/types.h>
20 #include <dirent.h>
21 #include <sys/time.h>
22
23 #ifdef __linux__
24 # define OPENFLAG_LARGEFILE (O_LARGEFILE)
25 #else
26 # define OPENFLAG_LARGEFILE (0)
27 #endif
28
29 static Mutex smbMutex;
30 static SMBCCTX* smbCTX = 0;
31 static FSCInfo* fscInfo = 0;
32 static FSSmbParam* currentFsParam = 0;
33 static FSSmbParam lastFsParam;
34
35 #define FREPARE_SMB_OPER(lockname, infoname, param) MutexLock lockname(&smbMutex); fscInfo = infoname; currentFsParam = param;
36
37
38 struct PathBuffer
39 {
40 std::vector<char> p;
41 int size;
42 int minPos;
43 int pos;
44
45 PathBuffer();
ClearPathBuffer46 void Clear() { pos = minPos; p[pos] = 0; }
47
48 void Cut( const char* s );
49 char* Set( const char* path );
SetPathPathBuffer50 char* SetPath( FSPath& path ) {return Set( ( char* ) path.GetString( CS_UTF8, '/' ) ); }
51 };
52
53
PathBuffer()54 PathBuffer::PathBuffer()
55 : p( 16 ), size( 16 ), pos( 0 )
56 {
57 strcpy( p.data(), "smb://" );
58 minPos = pos = strlen( p.data() );
59 }
60
Cut(const char * s)61 void PathBuffer::Cut( const char* s )
62 {
63 int l = strlen( s );
64 int nsize = pos + l + 1;
65
66 if ( nsize > size )
67 {
68 nsize = ( ( nsize + 0x100 - 1 ) / 0x100 ) * 0x100;
69 std::vector<char> t( nsize );
70
71 if ( pos > 0 ) { memcpy( t.data(), p.data(), pos ); }
72
73 t[pos] = 0;
74 p = t;
75 size = nsize;
76 }
77
78 memcpy( p.data() + pos, s, l + 1 );
79 pos += l;
80 }
81
Set(const char * path)82 char* PathBuffer::Set( const char* path )
83 {
84 Clear();
85
86 if ( !currentFsParam ) { return p.data(); }
87
88 if ( currentFsParam->server[0] )
89 {
90 if ( currentFsParam->user[0] )
91 {
92 Cut( const_cast<char*>( currentFsParam->user ) );
93 Cut( "@" );
94 }
95
96 Cut( const_cast<char*>( currentFsParam->server ) );
97 Cut( "/" );
98 }
99
100 if ( path )
101 {
102 if ( path[0] == '/' ) { path++; }
103
104 Cut( path );
105 }
106
107 return p.data();
108 }
109
110 static PathBuffer pathBuffer1;
111 static PathBuffer pathBuffer2;
112
SetString(char * dest,int len,const char * src)113 static void SetString( char* dest, int len, const char* src )
114 {
115 for ( ; *src && len > 1; dest++, src++, len-- ) { *dest = *src; }
116
117 if ( len > 0 ) { *dest = 0; }
118 }
119
120 //static int authIteration = 0; // 0 - search in cache >0 ask user
121 //static bool authCancelled;
122
smbcAuth(const char * srv,const char * shr,char * wg,int wglen,char * un,int unlen,char * pw,int pwlen)123 static void smbcAuth( const char* srv, const char* shr, char* wg, int wglen, char* un, int unlen, char* pw, int pwlen )
124 {
125 if ( !currentFsParam->server[0] ) //ходим по сети
126 {
127 return;
128 }
129
130 //printf("Auth! %s %s %s(%i) %s(%i)\n", srv, shr, wg, wglen, un, unlen);
131 //printf("currentFsParam->user = '%s'\n", currentFsParam->user);
132
133 if ( !currentFsParam->isSet )
134 {
135 FSSmbParam param = ( !currentFsParam->user[0] || !strcmp( const_cast<char*>( currentFsParam->user ), const_cast<char*>( lastFsParam.user ) ) ) &&
136 ( !currentFsParam->domain[0] || !strcmp( const_cast<char*>( currentFsParam->domain ), const_cast<char*>( lastFsParam.domain ) ) )
137 ? lastFsParam : *currentFsParam;
138 strcpy( const_cast<char*>( param.server ), const_cast<char*>( currentFsParam->server ) );
139
140 if ( !param.user[0] && unlen > 0 ) { SetString( const_cast<char*>( param.user ), sizeof( param.user ), un ); }
141
142 if ( !param.domain[0] && wglen > 0 ) { SetString( const_cast<char*>( param.domain ), sizeof( param.domain ), wg ); }
143
144 if ( fscInfo && fscInfo->SmbLogon( ¶m ) )
145 {
146 lastFsParam = *currentFsParam = param;
147 }
148 else
149 {
150 //...
151 return;
152 }
153 }
154
155 if ( currentFsParam->user[0] )
156 {
157 SetString( wg, wglen, const_cast<char*>( currentFsParam->domain ) );
158 SetString( un, unlen, const_cast<char*>( currentFsParam->user ) );
159 SetString( pw, pwlen, const_cast<char*>( currentFsParam->pass ) );
160 }
161
162 return;
163 }
164
InitSmb()165 static void InitSmb()
166 {
167 MutexLock lock( &smbMutex );
168
169 if ( smbCTX ) { return; }
170
171 smbCTX = smbc_new_context();
172
173 if ( !smbCTX ) { throw_syserr( 0, "smbclient can`t allocate context" ); }
174
175 if ( !smbc_init_context( smbCTX ) )
176 {
177 smbc_free_context( smbCTX, 0 );
178 smbCTX = 0;
179 throw_syserr( 0, "smbclient can`t init context" );
180 }
181
182 smbc_set_context( smbCTX );
183 smbc_setFunctionAuthData( smbCTX, smbcAuth );
184 smbc_setOptionUrlEncodeReaddirEntries( smbCTX, 0 );
185 }
186
FSSmb(FSSmbParam * param)187 FSSmb::FSSmb( FSSmbParam* param )
188 : FS( SAMBA )
189 {
190 InitSmb();
191
192 if ( param ) { _param = *param; }
193 }
194
Flags()195 unsigned FSSmb::Flags() { return HAVE_READ | HAVE_WRITE | HAVE_SEEK; };
IsEEXIST(int err)196 bool FSSmb::IsEEXIST( int err ) { return err == EEXIST; }
IsENOENT(int err)197 bool FSSmb::IsENOENT( int err ) { return err == ENOENT; }
IsEXDEV(int err)198 bool FSSmb::IsEXDEV( int err ) { return err == EXDEV; }
199
StrError(int err)200 FSString FSSmb::StrError( int err )
201 {
202 sys_char_t buf[1024];
203 FSString ret( sys_charset_id, ( char* )sys_error_str( err, buf, sizeof( buf ) ) );
204 return ret;
205 }
206
Equal(FS * fs)207 bool FSSmb::Equal( FS* fs )
208 {
209 if ( !fs || fs->Type() != FS::SAMBA ) { return false; }
210
211 return true;
212 }
213
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)214 int FSSmb::OpenRead( FSPath& path, int flags, int* err, FSCInfo* info )
215 {
216 FREPARE_SMB_OPER( lock, info, &_param );
217
218 int n = smbc_open( pathBuffer1.SetPath( path ), O_RDONLY | OPENFLAG_LARGEFILE, 0 );
219 SetError( err, errno );
220 return n < 0 ? -1 : n;
221 }
222
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)223 int FSSmb::OpenCreate( FSPath& path, bool overwrite, int mode, int flags, int* err, FSCInfo* info )
224 {
225 FREPARE_SMB_OPER( lock, info, &_param );
226
227 int n = smbc_open( pathBuffer1.SetPath( path ),
228 O_CREAT | O_WRONLY | O_TRUNC | OPENFLAG_LARGEFILE | ( overwrite ? 0 : O_EXCL ) , mode );
229 SetError( err, errno );
230 return n < 0 ? -1 : n;
231 }
232
Close(int fd,int * err,FSCInfo * info)233 int FSSmb::Close( int fd, int* err, FSCInfo* info )
234 {
235 FREPARE_SMB_OPER( lock, info, &_param );
236
237 if ( smbc_close( fd ) )
238 {
239 SetError( err, errno );
240 return -1;
241 }
242
243 return 0;
244 }
245
Read(int fd,void * buf,int size,int * err,FSCInfo * info)246 int FSSmb::Read( int fd, void* buf, int size, int* err, FSCInfo* info )
247 {
248 FREPARE_SMB_OPER( lock, info, &_param );
249
250 int n = smbc_read( fd, buf, size );
251
252 if ( n < 0 ) { SetError( err, errno ); return -1; }
253
254 return n;
255 }
256
Write(int fd,void * buf,int size,int * err,FSCInfo * info)257 int FSSmb::Write( int fd, void* buf, int size, int* err, FSCInfo* info )
258 {
259 FREPARE_SMB_OPER( lock, info, &_param );
260
261 int n = smbc_write( fd, buf, size );
262
263 if ( n < 0 ) { SetError( err, errno ); return -1; }
264
265 return n;
266 }
267
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)268 int FSSmb::Seek( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet, int* err, FSCInfo* info )
269 {
270 FREPARE_SMB_OPER( lock, info, &_param );
271
272 int whence = 0;
273
274 switch ( mode )
275 {
276 case FSEEK_POS:
277 whence = SEEK_CUR;
278 break;
279
280 case FSEEK_END:
281 whence = SEEK_END;
282 break;
283
284 case FSEEK_BEGIN:
285 whence = SEEK_SET;
286 break;
287
288 default:
289 whence = SEEK_SET;
290 };
291
292 seek_t n = smbc_lseek( fd, pos, whence );
293
294 if ( n < 0 ) { SetError( err, errno ); return -1; }
295
296 if ( pRet ) { *pRet = n; }
297
298 return 0;
299 }
300
301
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)302 int FSSmb::Rename( FSPath& oldpath, FSPath& newpath, int* err, FSCInfo* info )
303 {
304 FREPARE_SMB_OPER( lock, info, &_param );
305
306 int n = smbc_rename(
307 pathBuffer1.SetPath( oldpath ),
308 pathBuffer2.SetPath( newpath )
309 );
310 SetError( err, errno );
311 return n < 0 ? -1 : n;
312 }
313
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)314 int FSSmb::MkDir( FSPath& path, int mode, int* err, FSCInfo* info )
315 {
316 FREPARE_SMB_OPER( lock, info, &_param );
317
318 int n = smbc_mkdir( pathBuffer1.SetPath( path ), mode );
319 SetError( err, errno );
320 return n < 0 ? -1 : n;
321 }
322
Delete(FSPath & path,int * err,FSCInfo * info)323 int FSSmb::Delete( FSPath& path, int* err, FSCInfo* info )
324 {
325 FREPARE_SMB_OPER( lock, info, &_param );
326
327 int n = smbc_unlink( pathBuffer1.SetPath( path ) );
328 SetError( err, errno );
329 return n < 0 ? -1 : n;
330 }
331
RmDir(FSPath & path,int * err,FSCInfo * info)332 int FSSmb::RmDir( FSPath& path, int* err, FSCInfo* info )
333 {
334 FREPARE_SMB_OPER( lock, info, &_param );
335
336 int n = smbc_rmdir( pathBuffer1.SetPath( path ) );
337 SetError( err, errno );
338 return n < 0 ? -1 : n;
339 }
340
341
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)342 int FSSmb::SetFileTime( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info )
343 {
344 FREPARE_SMB_OPER( lock, info, &_param );
345
346 struct timeval tv[2];
347 tv[0].tv_sec = aTime;
348 tv[0].tv_usec = 0;
349 tv[1].tv_sec = mTime;
350 tv[1].tv_usec = 0;
351 int n = smbc_utimes( pathBuffer1.SetPath( path ), tv );
352 SetError( err, errno );
353 return n < 0 ? -1 : n;
354 }
355
356 //от глюков
357 struct big_stat
358 {
359 struct stat st;
360 char buf[0x100];
361 };
362
SMB_STAT(const char * url,struct stat * st)363 static int SMB_STAT( const char* url, struct stat* st )
364 {
365 big_stat s;
366 int r = smbc_stat( url, &s.st );
367 *st = s.st;
368 return r;
369 }
370
371
InternalStat(FSPath & path,FSStat * fsStat,FSCInfo * info)372 static int InternalStat( FSPath& path, FSStat* fsStat, FSCInfo* info )
373 {
374 struct stat st;
375
376 if ( SMB_STAT( pathBuffer1.SetPath( path ), &st ) )
377 {
378 return -1;
379 }
380
381 fsStat->mode = ( st.st_mode & ~( S_IXUSR | S_IXGRP | S_IXOTH ) );
382 fsStat->size = st.st_size;
383 fsStat->m_CreationTime = st.st_ctime;
384 fsStat->m_LastAccessTime = st.st_atime;
385 fsStat->m_LastWriteTime = st.st_mtime;
386 fsStat->m_ChangeTime = st.st_mtime;
387 fsStat->gid = st.st_gid;
388 fsStat->uid = st.st_uid;
389 fsStat->dev = st.st_dev;
390 fsStat->ino = st.st_ino;
391 return 0;
392 }
393
394
Stat(FSPath & path,FSStat * fsStat,int * err,FSCInfo * info)395 int FSSmb::Stat( FSPath& path, FSStat* fsStat, int* err, FSCInfo* info )
396 {
397 FREPARE_SMB_OPER( lock, info, &_param );
398 ASSERT( fsStat );
399
400 if ( InternalStat( path, fsStat, info ) )
401 {
402 SetError( err, errno );
403 return -1;
404 }
405
406 return 0;
407 }
408
SMB_FSTAT(int fd,struct stat * st)409 static int SMB_FSTAT( int fd, struct stat* st )
410 {
411 big_stat s;
412 int r = smbc_fstat( fd, &s.st );
413 *st = s.st;
414 return r;
415 }
416
417
InternalFStat(int fd,FSStat * fsStat,FSCInfo * info)418 static int InternalFStat( int fd, FSStat* fsStat, FSCInfo* info )
419 {
420 struct stat st;
421
422 if ( SMB_FSTAT( fd, &st ) )
423 {
424 return -1;
425 }
426
427 fsStat->mode = ( st.st_mode & ~( S_IXUSR | S_IXGRP | S_IXOTH ) );
428 fsStat->size = st.st_size;
429 fsStat->m_CreationTime = st.st_ctime;
430 fsStat->m_LastAccessTime = st.st_atime;
431 fsStat->m_LastWriteTime = st.st_mtime;
432 fsStat->m_ChangeTime = st.st_mtime;
433 fsStat->gid = st.st_gid;
434 fsStat->uid = st.st_uid;
435 fsStat->dev = st.st_dev;
436 fsStat->ino = st.st_ino;
437 return 0;
438 }
439
440
FStat(int fd,FSStat * st,int * err,FSCInfo * info)441 int FSSmb::FStat( int fd, FSStat* st, int* err, FSCInfo* info )
442 {
443 FREPARE_SMB_OPER( lock, info, &_param );
444 ASSERT( st );
445
446 if ( InternalFStat( fd, st, info ) )
447 {
448 SetError( err, errno );
449 return -1;
450 }
451
452 return 0;
453 }
454
455
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)456 int FSSmb::Symlink( FSPath& path, FSString& str, int* err, FSCInfo* info ) //EPERM
457 {
458 SetError( err, EPERM );
459 return -1;
460 }
461
StatVfs(FSPath & path,FSStatVfs * vst,int * err,FSCInfo * info)462 int FSSmb::StatVfs( FSPath& path, FSStatVfs* vst, int* err, FSCInfo* info )
463 {
464
465 SetError( err, 0 );
466 return -1;
467 /*
468
469 FREPARE_SMB_OPER(lock, info, &_param);
470 ASSERT(vst);
471
472 struct statvfs st;
473 memset(&st, 0, sizeof(st));
474
475 if (smbc_statvfs(pathBuffer1.SetPath(path), &st))
476 {
477 SetError(err, errno);
478 return -1;
479 }
480
481 ////////////// работает, но при первом вызове выдает "no talloc stackframe around, leaking memory", ХЗ
482 if (!st.f_frsize) st.f_frsize = st.f_bsize;
483
484 vst->size = int64_t(st.f_blocks) * st.f_frsize;
485 vst->avail = int64_t(st.f_bavail) * st.f_bsize;
486
487 return 0;
488 */
489 }
490
Uri(FSPath & path)491 FSString FSSmb::Uri( FSPath& path )
492 {
493 MutexLock lock( &mutex );
494 std::string a;
495
496 if ( _param.server[0] )
497 {
498 if ( _param.user[0] )
499 {
500 a = std::string( "smb://" ) + std::string( (char*)_param.user ) + "@" + std::string( (char*)_param.server ) + path.GetUtf8();
501 }
502 else
503 {
504 a = std::string( "smb://" ) + std::string( (char*)_param.server ) + path.GetUtf8();
505 }
506 }
507 else
508 {
509 a = std::string( "smb:/" ) + path.GetUtf8();
510 }
511
512 return FSString( CS_UTF8, a.c_str() );
513 }
514
ReadDir(FSList * list,FSPath & _path,int * err,FSCInfo * info)515 int FSSmb::ReadDir( FSList* list, FSPath& _path, int* err, FSCInfo* info )
516 {
517 FREPARE_SMB_OPER( lock, info, &_param );
518
519 if ( info && info->IsStopped() ) { return -2; }
520
521 list->Clear();
522
523 FSPath path( _path );
524
525 int d = smbc_opendir( pathBuffer1.SetPath( path ) );
526
527 if ( d < 0 )
528 {
529 SetError( err, errno );
530 return -1;
531 }
532
533 try
534 {
535 struct smbc_dirent* pEnt;
536
537 int n = path.Count();
538
539 while ( true )
540 {
541 if ( info && info->IsStopped() )
542 {
543 smbc_closedir( d );
544 return -2;
545 }
546
547 pEnt = smbc_readdir( d );
548
549 if ( !pEnt )
550 {
551 //???
552 break;
553 }
554
555 //skip . and ..
556 if ( pEnt->name[0] == '.' && ( !pEnt->name[1] || ( pEnt->name[1] == '.' && !pEnt->name[2] ) ) )
557 {
558 continue;
559 }
560
561
562 if ( //ignore it
563 pEnt->smbc_type == SMBC_PRINTER_SHARE ||
564 pEnt->smbc_type == SMBC_IPC_SHARE
565 ) { continue; }
566
567
568 clPtr<FSNode> pNode = new FSNode();
569 path.SetItem( n, CS_UTF8, pEnt->name );
570
571 switch ( pEnt->smbc_type )
572 {
573 case SMBC_WORKGROUP:
574 pNode->extType = FSNode::WORKGROUP;
575 pNode->st.mode = S_IFDIR;
576 break;
577
578 case SMBC_SERVER:
579 pNode->extType = FSNode::SERVER;
580 pNode->st.mode = S_IFDIR;
581 break;
582
583 case SMBC_FILE_SHARE:
584 pNode->extType = FSNode::FILESHARE;
585 pNode->st.mode = S_IFDIR;
586 break;
587
588 case SMBC_PRINTER_SHARE:
589 case SMBC_COMMS_SHARE:
590 case SMBC_IPC_SHARE:
591 case SMBC_DIR:
592 pNode->st.mode = S_IFDIR;
593 break;
594
595 default:
596 InternalStat( path, &pNode->st, info );
597 }
598
599 #if defined(__APPLE__)
600 if ( sys_charset_id == CS_UTF8 )
601 {
602 std::string normname = normalize_utf8_NFC( pEnt->name );
603 pNode->name.Set( sys_charset_id, normname.data() );
604 }
605 else
606 {
607 pNode->name.Set( sys_charset_id, pEnt->name );
608 }
609 #else
610 pNode->name.Set( sys_charset_id, pEnt->name );
611 #endif
612
613 list->Append( pNode );
614 };
615
616 smbc_closedir( d );
617
618 return 0;
619 }
620 catch ( ... )
621 {
622 smbc_closedir( d );
623 throw;
624 }
625 }
626
~FSSmb()627 FSSmb::~FSSmb()
628 {
629 }
630
631 #endif //LIBSMBCLIENT_EXIST
632