Merge pull request #26287 from CrystalP/ref-savefilestatejob
[xbmc.git] / xbmc / platform / posix / filesystem / SMBFile.cpp
blob8e0eff2d372f2b7947fff710fb689145e227a417
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 // SMBFile.cpp: implementation of the CSMBFile class.
11 //////////////////////////////////////////////////////////////////////
13 #include "SMBFile.h"
15 #include "PasswordManager.h"
16 #include "SMBDirectory.h"
17 #include "ServiceBroker.h"
18 #include "Util.h"
19 #include "commons/Exception.h"
20 #include "filesystem/SpecialProtocol.h"
21 #include "network/DNSNameCache.h"
22 #include "settings/AdvancedSettings.h"
23 #include "settings/Settings.h"
24 #include "settings/SettingsComponent.h"
25 #include "utils/StringUtils.h"
26 #include "utils/TimeUtils.h"
27 #include "utils/URIUtils.h"
28 #include "utils/log.h"
30 #include <cstring>
31 #include <inttypes.h>
32 #include <mutex>
33 #include <regex>
35 #include <libsmbclient.h>
37 using namespace XFILE;
39 void xb_smbc_log(void* private_ptr, int level, const char* msg)
41 const int logLevel = [level]()
43 switch (level)
45 case 0:
46 return LOGWARNING;
47 case 1:
48 return LOGINFO;
49 default:
50 return LOGDEBUG;
52 }();
54 if (std::strchr(msg, '@'))
56 // redact User/pass in URLs
57 static const std::regex redact("(\\w+://)\\S+:\\S+@");
58 CLog::Log(logLevel, "smb: {}", std::regex_replace(msg, redact, "$1USERNAME:PASSWORD@"));
60 else
61 CLog::Log(logLevel, "smb: {}", msg);
64 void xb_smbc_auth(const char *srv, const char *shr, char *wg, int wglen,
65 char *un, int unlen, char *pw, int pwlen)
69 // WTF is this ?, we get the original server cache only
70 // to set the server cache to this function which call the
71 // original one anyway. Seems quite silly.
72 smbc_get_cached_srv_fn orig_cache;
73 SMBCSRV* xb_smbc_cache(SMBCCTX* c, const char* server, const char* share, const char* workgroup, const char* username)
75 return orig_cache(c, server, share, workgroup, username);
78 bool CSMB::IsFirstInit = true;
80 CSMB::CSMB()
82 m_context = NULL;
83 m_OpenConnections = 0;
84 m_IdleTimeout = 0;
87 CSMB::~CSMB()
89 Deinit();
92 void CSMB::Deinit()
94 std::unique_lock<CCriticalSection> lock(*this);
96 /* samba goes loco if deinited while it has some files opened */
97 if (m_context)
99 smbc_set_context(NULL);
100 smbc_free_context(m_context, 1);
101 m_context = NULL;
105 void CSMB::Init()
107 std::unique_lock<CCriticalSection> lock(*this);
109 if (!m_context)
111 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
113 // force libsmbclient to use our own smb.conf by overriding HOME
114 std::string truehome(getenv("HOME"));
115 setenv("HOME", CSpecialProtocol::TranslatePath("special://home").c_str(), 1);
117 // Create ~/.kodi/.smb/smb.conf. This file is used by libsmbclient.
118 // http://us1.samba.org/samba/docs/man/manpages-3/libsmbclient.7.html
119 // http://us1.samba.org/samba/docs/man/manpages-3/smb.conf.5.html
120 std::string smb_conf;
121 std::string home(getenv("HOME"));
122 URIUtils::RemoveSlashAtEnd(home);
123 smb_conf = home + "/.smb";
124 int result = mkdir(smb_conf.c_str(), 0755);
125 if (result == 0 || (errno == EEXIST && IsFirstInit))
127 smb_conf += "/smb.conf";
128 FILE* f = fopen(smb_conf.c_str(), "w");
129 if (f != NULL)
131 fprintf(f, "[global]\n");
133 fprintf(f, "\tlock directory = %s/.smb/\n", home.c_str());
135 // set minimum smbclient protocol version
136 switch (settings->GetInt(CSettings::SETTING_SMB_MINPROTOCOL))
138 case 0:
139 default:
140 break;
141 case 1:
142 fprintf(f, "\tclient min protocol = NT1\n");
143 break;
144 case 2:
145 fprintf(f, "\tclient min protocol = SMB2_02\n");
146 break;
147 case 21:
148 fprintf(f, "\tclient min protocol = SMB2_10\n");
149 break;
150 case 3:
151 fprintf(f, "\tclient min protocol = SMB3\n");
152 break;
155 // set maximum smbclient protocol version
156 switch (settings->GetInt(CSettings::SETTING_SMB_MAXPROTOCOL))
158 case 0:
159 default:
160 break;
161 case 1:
162 fprintf(f, "\tclient max protocol = NT1\n");
163 break;
164 case 2:
165 fprintf(f, "\tclient max protocol = SMB2_02\n");
166 break;
167 case 21:
168 fprintf(f, "\tclient max protocol = SMB2_10\n");
169 break;
170 case 3:
171 fprintf(f, "\tclient max protocol = SMB3\n");
172 break;
175 // set legacy security options
176 if (settings->GetBool(CSettings::SETTING_SMB_LEGACYSECURITY) && (settings->GetInt(CSettings::SETTING_SMB_MAXPROTOCOL) == 1))
178 fprintf(f, "\tclient NTLMv2 auth = no\n");
179 fprintf(f, "\tclient use spnego = no\n");
182 // set wins server if there's one. name resolve order defaults to 'lmhosts host wins bcast'.
183 // if no WINS server has been specified the wins method will be ignored.
184 if (settings->GetString(CSettings::SETTING_SMB_WINSSERVER).length() > 0 && !StringUtils::EqualsNoCase(settings->GetString(CSettings::SETTING_SMB_WINSSERVER), "0.0.0.0") )
186 fprintf(f, "\twins server = %s\n", settings->GetString(CSettings::SETTING_SMB_WINSSERVER).c_str());
187 fprintf(f, "\tname resolve order = bcast wins host\n");
189 else
190 fprintf(f, "\tname resolve order = bcast host\n");
192 // use user-configured charset. if no charset is specified,
193 // samba tries to use charset 850 but falls back to ASCII in case it is not available
194 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_sambadoscodepage.length() > 0)
195 fprintf(f, "\tdos charset = %s\n", CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_sambadoscodepage.c_str());
197 // include users configuration if available
198 fprintf(f, "\tinclude = %s/.smb/user.conf\n", home.c_str());
200 fclose(f);
204 // reads smb.conf so this MUST be after we create smb.conf
205 // multiple smbc_init calls are ignored by libsmbclient.
206 // note: this is important as it initializes the smb old
207 // interface compatibility. Samba 3.4.0 or higher has the new interface.
208 // note: we leak the following here once, not sure why yet.
209 // 48 bytes -> smb_xmalloc_array
210 // 32 bytes -> set_param_opt
211 // 16 bytes -> set_param_opt
212 smbc_init(xb_smbc_auth, 0);
214 // setup our context
215 m_context = smbc_new_context();
217 // restore HOME
218 setenv("HOME", truehome.c_str(), 1);
220 #ifdef DEPRECATED_SMBC_INTERFACE
221 smbc_setDebug(m_context, CServiceBroker::GetLogging().CanLogComponent(LOGSAMBA) ? 10 : 0);
222 smbc_setLogCallback(m_context, this, xb_smbc_log);
223 smbc_setFunctionAuthData(m_context, xb_smbc_auth);
224 orig_cache = smbc_getFunctionGetCachedServer(m_context);
225 smbc_setFunctionGetCachedServer(m_context, xb_smbc_cache);
226 smbc_setOptionOneSharePerServer(m_context, false);
227 smbc_setOptionBrowseMaxLmbCount(m_context, 0);
228 smbc_setTimeout(m_context, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_sambaclienttimeout * 1000);
229 // we do not need to strdup these, smbc_setXXX below will make their own copies
230 if (settings->GetString(CSettings::SETTING_SMB_WORKGROUP).length() > 0)
231 //! @bug libsmbclient < 4.9 isn't const correct
232 smbc_setWorkgroup(m_context, const_cast<char*>(settings->GetString(CSettings::SETTING_SMB_WORKGROUP).c_str()));
233 std::string guest = "guest";
234 //! @bug libsmbclient < 4.8 isn't const correct
235 smbc_setUser(m_context, const_cast<char*>(guest.c_str()));
236 #else
237 m_context->debug = (CServiceBroker::GetLogging().CanLogComponent(LOGSAMBA) ? 10 : 0);
238 m_context->callbacks.auth_fn = xb_smbc_auth;
239 orig_cache = m_context->callbacks.get_cached_srv_fn;
240 m_context->callbacks.get_cached_srv_fn = xb_smbc_cache;
241 m_context->options.one_share_per_server = false;
242 m_context->options.browse_max_lmb_count = 0;
243 m_context->timeout = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_sambaclienttimeout * 1000;
244 // we need to strdup these, they will get free'd on smbc_free_context
245 if (settings->GetString(CSettings::SETTING_SMB_WORKGROUP).length() > 0)
246 m_context->workgroup = strdup(settings->GetString(CSettings::SETTING_SMB_WORKGROUP).c_str());
247 m_context->user = strdup("guest");
248 #endif
250 // initialize samba and do some hacking into the settings
251 if (smbc_init_context(m_context))
253 // setup context using the smb old interface compatibility
254 SMBCCTX *old_context = smbc_set_context(m_context);
255 // free previous context or we leak it, this comes from smbc_init above.
256 // there is a bug in smbclient (old interface), if we init/set a context
257 // then set(null)/free it in DeInit above, the next smbc_set_context
258 // return the already freed previous context, free again and bang, crash.
259 // so we setup a stic bool to track the first init so we can free the
260 // context associated with the initial smbc_init.
261 if (old_context && IsFirstInit)
263 smbc_free_context(old_context, 1);
264 IsFirstInit = false;
267 else
269 smbc_free_context(m_context, 1);
270 m_context = NULL;
273 m_IdleTimeout = 180;
276 std::string CSMB::URLEncode(const CURL &url)
278 /* due to smb wanting encoded urls we have to build it manually */
280 std::string flat = "smb://";
282 /* samba messes up of password is set but no username is set. don't know why yet */
283 /* probably the url parser that goes crazy */
284 if(url.GetUserName().length() > 0 /* || url.GetPassWord().length() > 0 */)
286 if(!url.GetDomain().empty())
288 flat += URLEncode(url.GetDomain());
289 flat += ";";
291 flat += URLEncode(url.GetUserName());
292 if(url.GetPassWord().length() > 0)
294 flat += ":";
295 flat += URLEncode(url.GetPassWord());
297 flat += "@";
299 flat += URLEncode(url.GetHostName());
301 if (url.HasPort())
303 flat += StringUtils::Format(":{}", url.GetPort());
306 /* okey sadly since a slash is an invalid name we have to tokenize */
307 std::vector<std::string> parts;
308 StringUtils::Tokenize(url.GetFileName(), parts, "/");
309 for (const std::string& it : parts)
311 flat += "/";
312 flat += URLEncode((it));
315 /* okey options should go here, thou current samba doesn't support any */
317 return flat;
320 std::string CSMB::URLEncode(const std::string &value)
322 return CURL::Encode(value);
325 /* This is called from CApplication::ProcessSlow() and is used to tell if smbclient have been idle for too long */
326 void CSMB::CheckIfIdle()
328 /* We check if there are open connections. This is done without a lock to not halt the mainthread. It should be thread safe as
329 worst case scenario is that m_OpenConnections could read 0 and then changed to 1 if this happens it will enter the if which will lead to another check, which is locked. */
330 if (m_OpenConnections == 0)
331 { /* I've set the the maximum IDLE time to be 1 min and 30 sec. */
332 std::unique_lock<CCriticalSection> lock(*this);
333 if (m_OpenConnections == 0 /* check again - when locked */ && m_context != NULL)
335 if (m_IdleTimeout > 0)
337 m_IdleTimeout--;
339 else
341 CLog::Log(LOGINFO, "Samba is idle. Closing the remaining connections");
342 smb.Deinit();
348 void CSMB::SetActivityTime()
350 /* Since we get called every 500ms from ProcessSlow we limit the tick count to 180 */
351 /* That means we have 2 ticks per second which equals 180/2 == 90 seconds */
352 m_IdleTimeout = 180;
355 /* The following two function is used to keep track on how many Opened files/directories there are.
356 This makes the idle timer not count if a movie is paused for example */
357 void CSMB::AddActiveConnection()
359 std::unique_lock<CCriticalSection> lock(*this);
360 m_OpenConnections++;
362 void CSMB::AddIdleConnection()
364 std::unique_lock<CCriticalSection> lock(*this);
365 m_OpenConnections--;
366 /* If we close a file we reset the idle timer so that we don't have any weird behaviours if a user
367 leaves the movie paused for a long while and then press stop */
368 m_IdleTimeout = 180;
371 CURL CSMB::GetResolvedUrl(const CURL& url)
373 CURL tmpUrl(url);
374 std::string resolvedHostName;
376 if (CDNSNameCache::Lookup(tmpUrl.GetHostName(), resolvedHostName))
377 tmpUrl.SetHostName(resolvedHostName);
379 return tmpUrl;
382 CSMB smb;
384 CSMBFile::CSMBFile()
386 smb.Init();
387 m_fd = -1;
388 smb.AddActiveConnection();
389 m_allowRetry = true;
392 CSMBFile::~CSMBFile()
394 Close();
395 smb.AddIdleConnection();
398 int64_t CSMBFile::GetPosition()
400 if (m_fd == -1)
401 return -1;
402 std::unique_lock<CCriticalSection> lock(smb);
403 if (!smb.IsSmbValid())
404 return -1;
405 return smbc_lseek(m_fd, 0, SEEK_CUR);
408 int64_t CSMBFile::GetLength()
410 if (m_fd == -1)
411 return -1;
412 return m_fileSize;
415 bool CSMBFile::Open(const CURL& url)
417 Close();
419 // we can't open files like smb://file.f or smb://server/file.f
420 // if a file matches the if below return false, it can't exist on a samba share.
421 if (!IsValidFile(url.GetFileName()))
423 CLog::Log(LOGINFO, "SMBFile->Open: Bad URL : '{}'", url.GetRedacted());
424 return false;
426 m_url = url;
428 // opening a file to another computer share will create a new session
429 // when opening smb://server xbms will try to find folder.jpg in all shares
430 // listed, which will create lot's of open sessions.
432 std::string strFileName;
433 m_fd = OpenFile(url, strFileName);
435 CLog::Log(LOGDEBUG, "CSMBFile::Open - opened {}, fd={}", url.GetRedacted(), m_fd);
436 if (m_fd == -1)
438 // write error to logfile
439 CLog::Log(LOGERROR, "SMBFile->Open: Unable to open file : '{}'\nunix_err:'{:x}' error : '{}'",
440 CURL::GetRedacted(strFileName), errno, strerror(errno));
441 return false;
444 std::unique_lock<CCriticalSection> lock(smb);
445 if (!smb.IsSmbValid())
446 return false;
447 struct stat tmpBuffer;
448 if (smbc_stat(strFileName.c_str(), &tmpBuffer) < 0)
450 smbc_close(m_fd);
451 m_fd = -1;
452 return false;
455 m_fileSize = tmpBuffer.st_size;
457 int64_t ret = smbc_lseek(m_fd, 0, SEEK_SET);
458 if ( ret < 0 )
460 smbc_close(m_fd);
461 m_fd = -1;
462 return false;
464 // We've successfully opened the file!
465 return true;
469 /// \brief Checks authentication against SAMBA share. Reads password cache created in CSMBDirectory::OpenDir().
470 /// \param strAuth The SMB style path
471 /// \return SMB file descriptor
473 int CSMBFile::OpenFile(std::string& strAuth)
475 int fd = -1;
477 std::string strPath = g_passwordManager.GetSMBAuthFilename(strAuth);
479 fd = smbc_open(strPath.c_str(), O_RDONLY, 0);
480 //! @todo Run a loop here that prompts for our username/password as appropriate?
481 //! We have the ability to run a file (eg from a button action) without browsing to
482 //! the directory first. In the case of a password protected share that we do
483 //! not have the authentication information for, the above smbc_open() will have
484 //! returned negative, and the file will not be opened. While this is not a particular
485 //! likely scenario, we might want to implement prompting for the password in this case.
486 //! The code from SMBDirectory can be used for this.
487 if(fd >= 0)
488 strAuth = strPath;
490 return fd;
494 int CSMBFile::OpenFile(const CURL &url, std::string& strAuth)
496 int fd = -1;
497 smb.Init();
499 strAuth = GetAuthenticatedPath(CSMB::GetResolvedUrl(url));
500 std::string strPath = strAuth;
503 std::unique_lock<CCriticalSection> lock(smb);
504 if (smb.IsSmbValid())
505 fd = smbc_open(strPath.c_str(), O_RDONLY, 0);
508 if (fd >= 0)
509 strAuth = strPath;
511 return fd;
514 bool CSMBFile::Exists(const CURL& url)
516 // we can't open files like smb://file.f or smb://server/file.f
517 // if a file matches the if below return false, it can't exist on a samba share.
518 if (!IsValidFile(url.GetFileName())) return false;
520 smb.Init();
521 std::string strFileName = GetAuthenticatedPath(CSMB::GetResolvedUrl(url));
523 struct stat info;
525 std::unique_lock<CCriticalSection> lock(smb);
526 if (!smb.IsSmbValid())
527 return false;
528 int iResult = smbc_stat(strFileName.c_str(), &info);
530 if (iResult < 0) return false;
531 return true;
534 int CSMBFile::Stat(struct __stat64* buffer)
536 if (m_fd == -1)
537 return -1;
539 struct stat tmpBuffer = {};
541 std::unique_lock<CCriticalSection> lock(smb);
542 if (!smb.IsSmbValid())
543 return -1;
544 int iResult = smbc_fstat(m_fd, &tmpBuffer);
545 CUtil::StatToStat64(buffer, &tmpBuffer);
546 return iResult;
549 int CSMBFile::Stat(const CURL& url, struct __stat64* buffer)
551 smb.Init();
552 std::string strFileName = GetAuthenticatedPath(CSMB::GetResolvedUrl(url));
553 std::unique_lock<CCriticalSection> lock(smb);
555 if (!smb.IsSmbValid())
556 return -1;
557 struct stat tmpBuffer = {};
558 int iResult = smbc_stat(strFileName.c_str(), &tmpBuffer);
559 CUtil::StatToStat64(buffer, &tmpBuffer);
560 return iResult;
563 int CSMBFile::Truncate(int64_t size)
565 if (m_fd == -1) return 0;
567 * This would force us to be dependant on SMBv3.2 which is GPLv3
568 * This is only used by the TagLib writers, which are not currently in use
569 * So log and warn until we implement TagLib writing & can re-implement this better.
570 std::unique_lock<CCriticalSection> lock(smb); // Init not called since it has to be "inited" by now
572 #if defined(TARGET_ANDROID)
573 int iResult = 0;
574 #else
575 int iResult = smbc_ftruncate(m_fd, size);
576 #endif
578 CLog::Log(LOGWARNING, "{} - Warning(smbc_ftruncate called and not implemented)", __FUNCTION__);
579 return 0;
582 ssize_t CSMBFile::Read(void *lpBuf, size_t uiBufSize)
584 if (uiBufSize > SSIZE_MAX)
585 uiBufSize = SSIZE_MAX;
587 if (m_fd == -1)
588 return -1;
590 // Some external libs (libass) use test read with zero size and
591 // null buffer pointer to check whether file is readable, but
592 // libsmbclient always return "-1" if called with null buffer
593 // regardless of buffer size.
594 // To overcome this, force return "0" in that case.
595 if (uiBufSize == 0 && lpBuf == NULL)
596 return 0;
598 std::unique_lock<CCriticalSection> lock(
599 smb); // Init not called since it has to be "inited" by now
600 if (!smb.IsSmbValid())
601 return -1;
602 smb.SetActivityTime();
604 ssize_t bytesRead = smbc_read(m_fd, lpBuf, (int)uiBufSize);
606 if (m_allowRetry && bytesRead < 0 && errno == EINVAL )
608 CLog::Log(LOGERROR, "{} - Error( {}, {}, {} ) - Retrying", __FUNCTION__, bytesRead, errno,
609 strerror(errno));
610 bytesRead = smbc_read(m_fd, lpBuf, (int)uiBufSize);
613 if ( bytesRead < 0 )
614 CLog::Log(LOGERROR, "{} - Error( {}, {}, {} )", __FUNCTION__, bytesRead, errno,
615 strerror(errno));
617 return bytesRead;
620 int64_t CSMBFile::Seek(int64_t iFilePosition, int iWhence)
622 if (m_fd == -1) return -1;
624 std::unique_lock<CCriticalSection> lock(
625 smb); // Init not called since it has to be "inited" by now
626 if (!smb.IsSmbValid())
627 return -1;
628 smb.SetActivityTime();
629 int64_t pos = smbc_lseek(m_fd, iFilePosition, iWhence);
631 if ( pos < 0 )
633 CLog::Log(LOGERROR, "{} - Error( {}, {}, {} )", __FUNCTION__, pos, errno, strerror(errno));
634 return -1;
637 return pos;
640 void CSMBFile::Close()
642 if (m_fd != -1)
644 CLog::Log(LOGDEBUG, "CSMBFile::Close closing fd {}", m_fd);
645 std::unique_lock<CCriticalSection> lock(smb);
646 if (!smb.IsSmbValid())
647 return;
648 smbc_close(m_fd);
650 m_fd = -1;
653 ssize_t CSMBFile::Write(const void* lpBuf, size_t uiBufSize)
655 if (m_fd == -1) return -1;
657 // lpBuf can be safely casted to void* since xbmc_write will only read from it.
658 std::unique_lock<CCriticalSection> lock(smb);
659 if (!smb.IsSmbValid())
660 return -1;
662 return smbc_write(m_fd, lpBuf, uiBufSize);
665 bool CSMBFile::Delete(const CURL& url)
667 smb.Init();
668 std::string strFile = GetAuthenticatedPath(CSMB::GetResolvedUrl(url));
670 std::unique_lock<CCriticalSection> lock(smb);
671 if (!smb.IsSmbValid())
672 return false;
674 int result = smbc_unlink(strFile.c_str());
676 if(result != 0)
677 CLog::Log(LOGERROR, "{} - Error( {} )", __FUNCTION__, strerror(errno));
679 return (result == 0);
682 bool CSMBFile::Rename(const CURL& url, const CURL& urlnew)
684 smb.Init();
685 std::string strFile = GetAuthenticatedPath(CSMB::GetResolvedUrl(url));
686 std::string strFileNew = GetAuthenticatedPath(CSMB::GetResolvedUrl(urlnew));
687 std::unique_lock<CCriticalSection> lock(smb);
688 if (!smb.IsSmbValid())
689 return false;
691 int result = smbc_rename(strFile.c_str(), strFileNew.c_str());
693 if(result != 0)
694 CLog::Log(LOGERROR, "{} - Error( {} )", __FUNCTION__, strerror(errno));
696 return (result == 0);
699 bool CSMBFile::OpenForWrite(const CURL& url, bool bOverWrite)
701 m_fileSize = 0;
703 Close();
705 // we can't open files like smb://file.f or smb://server/file.f
706 // if a file matches the if below return false, it can't exist on a samba share.
707 if (!IsValidFile(url.GetFileName())) return false;
709 std::string strFileName = GetAuthenticatedPath(CSMB::GetResolvedUrl(url));
710 std::unique_lock<CCriticalSection> lock(smb);
711 if (!smb.IsSmbValid())
712 return false;
714 if (bOverWrite)
716 CLog::Log(LOGWARNING, "SMBFile::OpenForWrite() called with overwriting enabled! - {}",
717 CURL::GetRedacted(strFileName));
718 m_fd = smbc_creat(strFileName.c_str(), 0);
720 else
722 m_fd = smbc_open(strFileName.c_str(), O_RDWR, 0);
725 if (m_fd == -1)
727 // write error to logfile
728 CLog::Log(LOGERROR, "SMBFile->Open: Unable to open file : '{}'\nunix_err:'{:x}' error : '{}'",
729 CURL::GetRedacted(strFileName), errno, strerror(errno));
730 return false;
733 // We've successfully opened the file!
734 return true;
737 bool CSMBFile::IsValidFile(const std::string& strFileName)
739 if (strFileName.find('/') == std::string::npos || /* doesn't have sharename */
740 StringUtils::EndsWith(strFileName, "/.") || /* not current folder */
741 StringUtils::EndsWith(strFileName, "/..")) /* not parent folder */
742 return false;
743 return true;
746 std::string CSMBFile::GetAuthenticatedPath(const CURL &url)
748 CURL authURL(CSMB::GetResolvedUrl(url));
749 CPasswordManager::GetInstance().AuthenticateURL(authURL);
750 return smb.URLEncode(authURL);
753 int CSMBFile::IoControl(IOControl request, void* param)
755 if (request == IOControl::SEEK_POSSIBLE)
756 return 1;
758 if (request == IOControl::SET_RETRY)
760 m_allowRetry = *(bool*) param;
761 return 0;
764 return -1;
767 int CSMBFile::GetChunkSize()
769 const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
771 if (!settings)
772 return (64 * 1024);
774 // Only SMBv2.1 and SMBv3 supports large MTU
775 if (settings->GetInt(CSettings::SETTING_SMB_MINPROTOCOL) > 2)
777 return (settings->GetInt(CSettings::SETTING_SMB_CHUNKSIZE) * 1024);
780 return (64 * 1024);