2 * Copyright (C) 2011-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 // FileNFS.cpp: implementation of the CNFSFile class.
23 //////////////////////////////////////////////////////////////////////
26 #ifdef HAS_FILESYSTEM_NFS
28 #include "threads/SingleLock.h"
29 #include "utils/log.h"
30 #include "utils/URIUtils.h"
31 #include "utils/StringUtils.h"
32 #include "network/DNSNameCache.h"
33 #include "threads/SystemClock.h"
35 #include <nfsc/libnfs-raw-mount.h>
42 //KEEP_ALIVE_TIMEOUT is decremented every half a second
43 //360 * 0.5s == 180s == 3mins
44 //so when no read was done for 3mins and files are open
45 //do the nfs keep alive for the open files
46 #define KEEP_ALIVE_TIMEOUT 360
48 //6 mins (360s) cached context timeout
49 #define CONTEXT_TIMEOUT 360000
51 //return codes for getContextForExport
52 #define CONTEXT_INVALID 0 //getcontext failed
53 #define CONTEXT_NEW 1 //new context created
54 #define CONTEXT_CACHED 2 //context cached and therefore already mounted (no new mount needed)
56 using namespace XFILE;
58 CNfsConnection::CNfsConnection()
62 , m_resolvedHostName("")
65 , m_OpenConnections(0)
67 , m_lastAccessedTime(0)
68 , m_pLibNfs(new DllLibNfs())
72 CNfsConnection::~CNfsConnection()
78 void CNfsConnection::resolveHost(const CURL &url)
80 //resolve if hostname has changed
81 CDNSNameCache::Lookup(url.GetHostName(), m_resolvedHostName);
84 std::list<std::string> CNfsConnection::GetExportList(const CURL &url)
86 std::list<std::string> retList;
90 struct exportnode *exportlist, *tmp;
91 exportlist = m_pLibNfs->mount_getexports(m_resolvedHostName);
94 for(tmp = exportlist; tmp!=NULL; tmp=tmp->ex_next)
96 std::string exportStr = std::string(tmp->ex_dir);
98 retList.push_back(exportStr);
101 gNfsConnection.GetImpl()->mount_free_export_list(exportlist);
109 bool CNfsConnection::HandleDyLoad()
113 if(!m_pLibNfs->IsLoaded())
115 if(!m_pLibNfs->Load())
117 CLog::Log(LOGERROR,"NFS: Error loading libnfs (%s).",__FUNCTION__);
124 void CNfsConnection::clearMembers()
126 m_exportPath.clear();
128 m_exportList.clear();
129 m_writeChunkSize = 0;
131 m_pNfsContext = NULL;
132 m_KeepAliveTimeouts.clear();
135 void CNfsConnection::destroyOpenContexts()
137 CSingleLock lock(openContextLock);
138 for(tOpenContextMap::iterator it = m_openContextMap.begin();it!=m_openContextMap.end();it++)
140 m_pLibNfs->nfs_destroy_context(it->second.pContext);
142 m_openContextMap.clear();
145 void CNfsConnection::destroyContext(const CStdString &exportName)
147 CSingleLock lock(openContextLock);
148 tOpenContextMap::iterator it = m_openContextMap.find(exportName.c_str());
149 if (it != m_openContextMap.end())
151 m_pLibNfs->nfs_destroy_context(it->second.pContext);
152 m_openContextMap.erase(it);
156 struct nfs_context *CNfsConnection::getContextFromMap(const CStdString &exportname, bool forceCacheHit/* = false*/)
158 struct nfs_context *pRet = NULL;
159 CSingleLock lock(openContextLock);
161 tOpenContextMap::iterator it = m_openContextMap.find(exportname.c_str());
162 if(it != m_openContextMap.end())
164 //check if context has timed out already
165 uint64_t now = XbmcThreads::SystemClockMillis();
166 if((now - it->second.lastAccessedTime) < CONTEXT_TIMEOUT || forceCacheHit)
168 //its not timedout yet or caller wants the cached entry regardless of timeout
169 //refresh access time of that
170 //context and return it
171 if (!forceCacheHit) // only log it if this isn't the resetkeepalive on each read ;)
172 CLog::Log(LOGDEBUG, "NFS: Refreshing context for %s, old: %"PRId64", new: %"PRId64, exportname.c_str(), it->second.lastAccessedTime, now);
173 it->second.lastAccessedTime = now;
174 pRet = it->second.pContext;
178 //context is timed out
179 //destroy it and return NULL
180 CLog::Log(LOGDEBUG, "NFS: Old context timed out - destroying it");
181 m_pLibNfs->nfs_destroy_context(it->second.pContext);
182 m_openContextMap.erase(it);
188 int CNfsConnection::getContextForExport(const CStdString &exportname)
190 int ret = CONTEXT_INVALID;
196 m_pNfsContext = getContextFromMap(exportname);
200 CLog::Log(LOGDEBUG,"NFS: Context for %s not open - get a new context.", exportname.c_str());
201 m_pNfsContext = m_pLibNfs->nfs_init_context();
205 CLog::Log(LOGERROR,"NFS: Error initcontext in getContextForExport.");
209 struct contextTimeout tmp;
210 CSingleLock lock(openContextLock);
211 tmp.pContext = m_pNfsContext;
212 tmp.lastAccessedTime = XbmcThreads::SystemClockMillis();
213 m_openContextMap[exportname] = tmp; //add context to list of all contexts
219 ret = CONTEXT_CACHED;
220 CLog::Log(LOGDEBUG,"NFS: Using cached context.");
222 m_lastAccessedTime = XbmcThreads::SystemClockMillis(); //refresh last access time of m_pNfsContext
227 bool CNfsConnection::splitUrlIntoExportAndPath(const CURL& url, CStdString &exportPath, CStdString &relativePath)
229 //refresh exportlist if empty or hostname change
230 if(m_exportList.empty() || !StringUtils::EqualsNoCase(url.GetHostName(), m_hostName))
232 m_exportList = GetExportList(url);
235 return splitUrlIntoExportAndPath(url, exportPath, relativePath, m_exportList);
238 bool CNfsConnection::splitUrlIntoExportAndPath(const CURL& url,CStdString &exportPath, CStdString &relativePath, std::list<std::string> &exportList)
242 if(!exportList.empty())
247 CStdString path = url.GetFileName();
249 //GetFileName returns path without leading "/"
250 //but we need it because the export paths start with "/"
251 //and path.Find(*it) wouldn't work else
257 std::list<std::string>::iterator it;
259 for(it=exportList.begin();it!=exportList.end();it++)
261 //if path starts with the current export path
262 if(StringUtils::StartsWith(path, *it))
264 //its possible that StartsWith may not find the correct match first
265 //as an example, if /path/ & and /path/sub/ are exported, but
266 //the user specifies the path /path/subdir/ (from /path/ export).
267 //If the path is longer than the exportpath, make sure / is next.
268 if( (path.length() > (*it).length()) &&
269 (path[(*it).length()] != '/') && (*it) != "/")
272 //handle special case where root is exported
273 //in that case we don't want to stripp off to
275 if( exportPath == path )
277 else if( exportPath == "/" )
278 relativePath = "//" + path.substr(exportPath.length());
280 relativePath = "//" + path.substr(exportPath.length()+1);
289 bool CNfsConnection::Connect(const CURL& url, CStdString &relativePath)
291 CSingleLock lock(*this);
294 CStdString exportPath = "";
297 ret = splitUrlIntoExportAndPath(url, exportPath, relativePath);
299 if( (ret && (!exportPath.Equals(m_exportPath,true) ||
300 !url.GetHostName().Equals(m_hostName,false))) ||
301 (XbmcThreads::SystemClockMillis() - m_lastAccessedTime) > CONTEXT_TIMEOUT )
303 int contextRet = getContextForExport(url.GetHostName() + exportPath);
305 if(contextRet == CONTEXT_INVALID)//we need a new context because sharename or hostname has changed
310 if(contextRet == CONTEXT_NEW) //new context was created - we need to mount it
312 //we connect to the directory of the path. This will be the "root" path of this connection then.
313 //So all fileoperations are relative to this mountpoint...
314 nfsRet = m_pLibNfs->nfs_mount(m_pNfsContext, m_resolvedHostName.c_str(), exportPath.c_str());
318 CLog::Log(LOGERROR,"NFS: Failed to mount nfs share: %s (%s)\n", exportPath.c_str(), m_pLibNfs->nfs_get_error(m_pNfsContext));
319 destroyContext(url.GetHostName() + exportPath);
322 CLog::Log(LOGDEBUG,"NFS: Connected to server %s and export %s\n", url.GetHostName().c_str(), exportPath.c_str());
324 m_exportPath = exportPath;
325 m_hostName = url.GetHostName();
326 //read chunksize only works after mount
327 m_readChunkSize = m_pLibNfs->nfs_get_readmax(m_pNfsContext);
328 m_writeChunkSize = m_pLibNfs->nfs_get_writemax(m_pNfsContext);
330 if(contextRet == CONTEXT_NEW)
332 CLog::Log(LOGDEBUG,"NFS: chunks: r/w %i/%i\n", (int)m_readChunkSize,(int)m_writeChunkSize);
338 void CNfsConnection::Deinit()
340 if(m_pNfsContext && m_pLibNfs->IsLoaded())
342 destroyOpenContexts();
343 m_pNfsContext = NULL;
349 /* This is called from CApplication::ProcessSlow() and is used to tell if nfs have been idle for too long */
350 void CNfsConnection::CheckIfIdle()
352 /* We check if there are open connections. This is done without a lock to not halt the mainthread. It should be thread safe as
353 worst case scenario is that m_OpenConnections could read 0 and then changed to 1 if this happens it will enter the if wich will lead to another check, wich is locked. */
354 if (m_OpenConnections == 0 && m_pNfsContext != NULL)
355 { /* I've set the the maxiumum IDLE time to be 1 min and 30 sec. */
356 CSingleLock lock(*this);
357 if (m_OpenConnections == 0 /* check again - when locked */)
359 if (m_IdleTimeout > 0)
365 CLog::Log(LOGNOTICE, "NFS is idle. Closing the remaining connections.");
366 gNfsConnection.Deinit();
371 if( m_pNfsContext != NULL )
373 CSingleLock lock(keepAliveLock);
374 //handle keep alive on opened files
375 for( tFileKeepAliveMap::iterator it = m_KeepAliveTimeouts.begin();it!=m_KeepAliveTimeouts.end();it++)
377 if(it->second.refreshCounter > 0)
379 it->second.refreshCounter--;
383 keepAlive(it->second.exportPath, it->first);
385 resetKeepAlive(it->second.exportPath, it->first);
391 //remove file handle from keep alive list on file close
392 void CNfsConnection::removeFromKeepAliveList(struct nfsfh *_pFileHandle)
394 CSingleLock lock(keepAliveLock);
395 m_KeepAliveTimeouts.erase(_pFileHandle);
398 //reset timeouts on read
399 void CNfsConnection::resetKeepAlive(std::string _exportPath, struct nfsfh *_pFileHandle)
401 CSingleLock lock(keepAliveLock);
402 //refresh last access time of the context aswell
403 getContextFromMap(_exportPath, true);
404 //adds new keys - refreshs existing ones
405 m_KeepAliveTimeouts[_pFileHandle].exportPath = _exportPath;
406 m_KeepAliveTimeouts[_pFileHandle].refreshCounter = KEEP_ALIVE_TIMEOUT;
409 //keep alive the filehandles nfs connection
410 //by blindly doing a read 32bytes - seek back to where
412 void CNfsConnection::keepAlive(std::string _exportPath, struct nfsfh *_pFileHandle)
416 // this also refreshs the last accessed time for the context
417 // true forces a cachehit regardless the context is timedout
418 // on this call we are sure its not timedout even if the last accessed
420 struct nfs_context *pContext = getContextFromMap(_exportPath, true);
422 if (!pContext)// this should normally never happen - paranoia
423 pContext = m_pNfsContext;
425 CLog::Log(LOGNOTICE, "NFS: sending keep alive after %i s.",KEEP_ALIVE_TIMEOUT/2);
426 CSingleLock lock(*this);
427 m_pLibNfs->nfs_lseek(pContext, _pFileHandle, 0, SEEK_CUR, &offset);
428 m_pLibNfs->nfs_read(pContext, _pFileHandle, 32, buffer);
429 m_pLibNfs->nfs_lseek(pContext, _pFileHandle, offset, SEEK_SET, &offset);
432 int CNfsConnection::stat(const CURL &url, NFSSTAT *statbuff)
434 CSingleLock lock(*this);
436 CStdString exportPath;
437 CStdString relativePath;
438 struct nfs_context *pTmpContext = NULL;
447 if(splitUrlIntoExportAndPath(url, exportPath, relativePath))
449 pTmpContext = m_pLibNfs->nfs_init_context();
453 //we connect to the directory of the path. This will be the "root" path of this connection then.
454 //So all fileoperations are relative to this mountpoint...
455 nfsRet = m_pLibNfs->nfs_mount(pTmpContext, m_resolvedHostName.c_str(), exportPath.c_str());
459 nfsRet = m_pLibNfs->nfs_stat(pTmpContext, relativePath.c_str(), statbuff);
463 CLog::Log(LOGERROR,"NFS: Failed to mount nfs share: %s (%s)\n", exportPath.c_str(), m_pLibNfs->nfs_get_error(m_pNfsContext));
466 m_pLibNfs->nfs_destroy_context(pTmpContext);
467 CLog::Log(LOGDEBUG,"NFS: Connected to server %s and export %s in tmpContext\n", url.GetHostName().c_str(), exportPath.c_str());
473 /* The following two function is used to keep track on how many Opened files/directories there are.
474 needed for unloading the dylib*/
475 void CNfsConnection::AddActiveConnection()
477 CSingleLock lock(*this);
481 void CNfsConnection::AddIdleConnection()
483 CSingleLock lock(*this);
485 /* If we close a file we reset the idle timer so that we don't have any wierd behaviours if a user
486 leaves the movie paused for a long while and then press stop */
490 CNfsConnection gNfsConnection;
494 , m_pFileHandle(NULL)
495 , m_pNfsContext(NULL)
497 gNfsConnection.AddActiveConnection();
500 CNFSFile::~CNFSFile()
503 gNfsConnection.AddIdleConnection();
506 int64_t CNFSFile::GetPosition()
510 CSingleLock lock(gNfsConnection);
512 if (gNfsConnection.GetNfsContext() == NULL || m_pFileHandle == NULL) return 0;
514 ret = (int)gNfsConnection.GetImpl()->nfs_lseek(gNfsConnection.GetNfsContext(), m_pFileHandle, 0, SEEK_CUR, &offset);
518 CLog::Log(LOGERROR, "NFS: Failed to lseek(%s)",gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
523 int64_t CNFSFile::GetLength()
525 if (m_pFileHandle == NULL) return 0;
529 bool CNFSFile::Open(const CURL& url)
533 // we can't open files like nfs://file.f or nfs://server/file.f
534 // if a file matches the if below return false, it can't exist on a nfs share.
535 if (!IsValidFile(url.GetFileName()))
537 CLog::Log(LOGNOTICE,"NFS: Bad URL : '%s'",url.GetFileName().c_str());
541 CStdString filename = "";
543 CSingleLock lock(gNfsConnection);
545 if(!gNfsConnection.Connect(url, filename))
548 m_pNfsContext = gNfsConnection.GetNfsContext();
549 m_exportPath = gNfsConnection.GetContextMapId();
551 ret = gNfsConnection.GetImpl()->nfs_open(m_pNfsContext, filename.c_str(), O_RDONLY, &m_pFileHandle);
555 CLog::Log(LOGINFO, "CNFSFile::Open: Unable to open file : '%s' error : '%s'", url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(m_pNfsContext));
556 m_pNfsContext = NULL;
557 m_exportPath.clear();
561 CLog::Log(LOGDEBUG,"CNFSFile::Open - opened %s",url.GetFileName().c_str());
564 struct __stat64 tmpBuffer;
566 if( Stat(&tmpBuffer) )
573 m_fileSize = tmpBuffer.st_size;//cache the size of this file
574 // We've successfully opened the file!
579 bool CNFSFile::Exists(const CURL& url)
581 return Stat(url,NULL) == 0;
584 int CNFSFile::Stat(struct __stat64* buffer)
586 return Stat(m_url,buffer);
590 int CNFSFile::Stat(const CURL& url, struct __stat64* buffer)
593 CSingleLock lock(gNfsConnection);
594 CStdString filename = "";
596 if(!gNfsConnection.Connect(url,filename))
600 NFSSTAT tmpBuffer = {0};
602 ret = gNfsConnection.GetImpl()->nfs_stat(gNfsConnection.GetNfsContext(), filename.c_str(), &tmpBuffer);
604 //if buffer == NULL we where called from Exists - in that case don't spam the log with errors
605 if (ret != 0 && buffer != NULL)
607 CLog::Log(LOGERROR, "NFS: Failed to stat(%s) %s\n", url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
614 #if defined(TARGET_WINDOWS)// TODO get rid of this define after gotham
615 memcpy(buffer, &tmpBuffer, sizeof(struct __stat64));
617 memset(buffer, 0, sizeof(struct __stat64));
618 buffer->st_dev = tmpBuffer.st_dev;
619 buffer->st_ino = tmpBuffer.st_ino;
620 buffer->st_mode = tmpBuffer.st_mode;
621 buffer->st_nlink = tmpBuffer.st_nlink;
622 buffer->st_uid = tmpBuffer.st_uid;
623 buffer->st_gid = tmpBuffer.st_gid;
624 buffer->st_rdev = tmpBuffer.st_rdev;
625 buffer->st_size = tmpBuffer.st_size;
626 buffer->st_atime = tmpBuffer.st_atime;
627 buffer->st_mtime = tmpBuffer.st_mtime;
628 buffer->st_ctime = tmpBuffer.st_ctime;
635 unsigned int CNFSFile::Read(void *lpBuf, int64_t uiBufSize)
637 int numberOfBytesRead = 0;
638 CSingleLock lock(gNfsConnection);
640 if (m_pFileHandle == NULL || m_pNfsContext == NULL ) return 0;
642 numberOfBytesRead = gNfsConnection.GetImpl()->nfs_read(m_pNfsContext, m_pFileHandle, uiBufSize, (char *)lpBuf);
644 lock.Leave();//no need to keep the connection lock after that
646 gNfsConnection.resetKeepAlive(m_exportPath, m_pFileHandle);//triggers keep alive timer reset for this filehandle
648 //something went wrong ...
649 if (numberOfBytesRead < 0)
651 CLog::Log(LOGERROR, "%s - Error( %d, %s )", __FUNCTION__, numberOfBytesRead, gNfsConnection.GetImpl()->nfs_get_error(m_pNfsContext));
654 return (unsigned int)numberOfBytesRead;
657 int64_t CNFSFile::Seek(int64_t iFilePosition, int iWhence)
662 CSingleLock lock(gNfsConnection);
663 if (m_pFileHandle == NULL || m_pNfsContext == NULL) return -1;
666 ret = (int)gNfsConnection.GetImpl()->nfs_lseek(m_pNfsContext, m_pFileHandle, iFilePosition, iWhence, &offset);
669 CLog::Log(LOGERROR, "%s - Error( seekpos: %"PRId64", whence: %i, fsize: %"PRId64", %s)", __FUNCTION__, iFilePosition, iWhence, m_fileSize, gNfsConnection.GetImpl()->nfs_get_error(m_pNfsContext));
672 return (int64_t)offset;
675 int CNFSFile::Truncate(int64_t iSize)
679 CSingleLock lock(gNfsConnection);
680 if (m_pFileHandle == NULL || m_pNfsContext == NULL) return -1;
683 ret = (int)gNfsConnection.GetImpl()->nfs_ftruncate(m_pNfsContext, m_pFileHandle, iSize);
686 CLog::Log(LOGERROR, "%s - Error( ftruncate: %"PRId64", fsize: %"PRId64", %s)", __FUNCTION__, iSize, m_fileSize, gNfsConnection.GetImpl()->nfs_get_error(m_pNfsContext));
692 void CNFSFile::Close()
694 CSingleLock lock(gNfsConnection);
696 if (m_pFileHandle != NULL && m_pNfsContext != NULL)
699 CLog::Log(LOGDEBUG,"CNFSFile::Close closing file %s", m_url.GetFileName().c_str());
700 // remove it from keep alive list before closing
701 // so keep alive code doens't process it anymore
702 gNfsConnection.removeFromKeepAliveList(m_pFileHandle);
703 ret = gNfsConnection.GetImpl()->nfs_close(m_pNfsContext, m_pFileHandle);
707 CLog::Log(LOGERROR, "Failed to close(%s) - %s\n", m_url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(m_pNfsContext));
709 m_pFileHandle = NULL;
710 m_pNfsContext = NULL;
712 m_exportPath.clear();
717 //for nfs write to work we have to write chunked
718 //otherwise this could crash on big files
719 int CNFSFile::Write(const void* lpBuf, int64_t uiBufSize)
721 int numberOfBytesWritten = 0;
722 int writtenBytes = 0;
723 int64_t leftBytes = uiBufSize;
724 //clamp max write chunksize to 32kb - fixme - this might be superfluious with future libnfs versions
725 int64_t chunkSize = gNfsConnection.GetMaxWriteChunkSize() > 32768 ? 32768 : gNfsConnection.GetMaxWriteChunkSize();
727 CSingleLock lock(gNfsConnection);
729 if (m_pFileHandle == NULL || m_pNfsContext == NULL) return -1;
731 //write as long as some bytes are left to be written
734 //the last chunk could be smalle than chunksize
735 if(leftBytes < chunkSize)
737 chunkSize = leftBytes;//write last chunk with correct size
740 writtenBytes = gNfsConnection.GetImpl()->nfs_write(m_pNfsContext,
743 (char *)lpBuf + numberOfBytesWritten);
744 //decrease left bytes
745 leftBytes-= writtenBytes;
746 //increase overall written bytes
747 numberOfBytesWritten += writtenBytes;
749 //danger - something went wrong
750 if (writtenBytes < 0)
752 CLog::Log(LOGERROR, "Failed to pwrite(%s) %s\n", m_url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(m_pNfsContext));
756 //return total number of written bytes
757 return numberOfBytesWritten;
760 bool CNFSFile::Delete(const CURL& url)
763 CSingleLock lock(gNfsConnection);
764 CStdString filename = "";
766 if(!gNfsConnection.Connect(url, filename))
770 ret = gNfsConnection.GetImpl()->nfs_unlink(gNfsConnection.GetNfsContext(), filename.c_str());
774 CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
779 bool CNFSFile::Rename(const CURL& url, const CURL& urlnew)
782 CSingleLock lock(gNfsConnection);
783 CStdString strFile = "";
785 if(!gNfsConnection.Connect(url,strFile))
788 CStdString strFileNew;
790 gNfsConnection.splitUrlIntoExportAndPath(urlnew, strDummy, strFileNew);
792 ret = gNfsConnection.GetImpl()->nfs_rename(gNfsConnection.GetNfsContext() , strFile.c_str(), strFileNew.c_str());
796 CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
801 bool CNFSFile::OpenForWrite(const CURL& url, bool bOverWrite)
804 // we can't open files like nfs://file.f or nfs://server/file.f
805 // if a file matches the if below return false, it can't exist on a nfs share.
806 if (!IsValidFile(url.GetFileName())) return false;
809 CSingleLock lock(gNfsConnection);
810 CStdString filename = "";
812 if(!gNfsConnection.Connect(url,filename))
815 m_pNfsContext = gNfsConnection.GetNfsContext();
816 m_exportPath = gNfsConnection.GetContextMapId();
820 CLog::Log(LOGWARNING, "FileNFS::OpenForWrite() called with overwriting enabled! - %s", filename.c_str());
821 //create file with proper permissions
822 ret = gNfsConnection.GetImpl()->nfs_creat(m_pNfsContext, filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &m_pFileHandle);
823 //if file was created the file handle isn't valid ... so close it and open later
826 gNfsConnection.GetImpl()->nfs_close(m_pNfsContext,m_pFileHandle);
827 m_pFileHandle = NULL;
831 ret = gNfsConnection.GetImpl()->nfs_open(m_pNfsContext, filename.c_str(), O_RDWR, &m_pFileHandle);
833 if (ret || m_pFileHandle == NULL)
835 // write error to logfile
836 CLog::Log(LOGERROR, "CNFSFile::Open: Unable to open file : '%s' error : '%s'", filename.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
837 m_pNfsContext = NULL;
838 m_exportPath.clear();
843 struct __stat64 tmpBuffer = {0};
845 //only stat if file was not created
854 m_fileSize = tmpBuffer.st_size;//cache filesize of this file
856 else//file was created - filesize is zero
861 // We've successfully opened the file!
865 bool CNFSFile::IsValidFile(const CStdString& strFileName)
867 if (strFileName.find('/') == std::string::npos || /* doesn't have sharename */
868 StringUtils::EndsWith(strFileName, "/.") || /* not current folder */
869 StringUtils::EndsWith(strFileName, "/..")) /* not parent folder */
873 #endif//HAS_FILESYSTEM_NFS