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( &param ) )
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