Skip to content
Browse files

[nfs] - add nfs support (incl. write allowed like smb)

- using dyloaded libnfs - incl. connection tracking for delayed unload feature
  • Loading branch information...
1 parent 9ed84f2 commit 9ec9cab31a5b838efadb0fe34e14c92ed52cfdb2 @Memphiz committed Jun 11, 2011
View
15 XBMC.xcodeproj/project.pbxproj
@@ -602,6 +602,8 @@
C8D0B2AF1265A9A800F0C0AC /* SystemGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8D0B2AE1265A9A800F0C0AC /* SystemGlobals.cpp */; };
C8D0B2B01265A9A800F0C0AC /* SystemGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8D0B2AE1265A9A800F0C0AC /* SystemGlobals.cpp */; };
C8EC5D0E1369519D00CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D0C1369519D00CCC10D /* XBMC_keytable.cpp */; };
+ DF0DF15B13A3ADA7008ED511 /* FileNFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF15713A3ADA7008ED511 /* FileNFS.cpp */; };
+ DF0DF15C13A3ADA7008ED511 /* NFSDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF15913A3ADA7008ED511 /* NFSDirectory.cpp */; };
E306D12E0DDF7B590052C2AD /* XBMCHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E306D12C0DDF7B590052C2AD /* XBMCHelper.cpp */; };
E33206380D5070AA00435CE3 /* DVDDemuxVobsub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E33206370D5070AA00435CE3 /* DVDDemuxVobsub.cpp */; };
E33466A60D2E5103005A65EC /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E33466A50D2E5103005A65EC /* IOKit.framework */; };
@@ -2518,6 +2520,10 @@
C8D0B2AE1265A9A800F0C0AC /* SystemGlobals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemGlobals.cpp; sourceTree = "<group>"; };
C8EC5D0C1369519D00CCC10D /* XBMC_keytable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMC_keytable.cpp; sourceTree = "<group>"; };
C8EC5D0D1369519D00CCC10D /* XBMC_keytable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMC_keytable.h; sourceTree = "<group>"; };
+ DF0DF15713A3ADA7008ED511 /* FileNFS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileNFS.cpp; sourceTree = "<group>"; };
+ DF0DF15813A3ADA7008ED511 /* FileNFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileNFS.h; sourceTree = "<group>"; };
+ DF0DF15913A3ADA7008ED511 /* NFSDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NFSDirectory.cpp; sourceTree = "<group>"; };
+ DF0DF15A13A3ADA7008ED511 /* NFSDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NFSDirectory.h; sourceTree = "<group>"; };
E306D12C0DDF7B590052C2AD /* XBMCHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMCHelper.cpp; sourceTree = "<group>"; };
E306D12D0DDF7B590052C2AD /* XBMCHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCHelper.h; sourceTree = "<group>"; };
E33206370D5070AA00435CE3 /* DVDDemuxVobsub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDDemuxVobsub.cpp; sourceTree = "<group>"; };
@@ -5521,6 +5527,10 @@
E38E16940D25F9FA00618676 /* filesystem */ = {
isa = PBXGroup;
children = (
+ DF0DF15713A3ADA7008ED511 /* FileNFS.cpp */,
+ DF0DF15813A3ADA7008ED511 /* FileNFS.h */,
+ DF0DF15913A3ADA7008ED511 /* NFSDirectory.cpp */,
+ DF0DF15A13A3ADA7008ED511 /* NFSDirectory.h */,
7C84A59C12FA3C1600CD1714 /* SourcesDirectory.cpp */,
7C84A59D12FA3C1600CD1714 /* SourcesDirectory.h */,
7C2D6AE20F35453E00DD2E85 /* SpecialProtocol.cpp */,
@@ -8063,6 +8073,11 @@
F558F27B13ABD56600631E12 /* DirtyRegionSolvers.cpp in Sources */,
F558F27F13ABD57400631E12 /* DirtyRegionTracker.cpp in Sources */,
F558F29613ABD7DF00631E12 /* GUIWindowDebugInfo.cpp in Sources */,
+ 7C0A7F7C13A9E69A00AFC2BD /* DirtyRegionSolvers.cpp in Sources */,
+ 7C0A7F7D13A9E69A00AFC2BD /* DirtyRegionTracker.cpp in Sources */,
+ 7C0A7F8213A9E6C600AFC2BD /* GUIWindowDebugInfo.cpp in Sources */,
+ DF0DF15B13A3ADA7008ED511 /* FileNFS.cpp in Sources */,
+ DF0DF15C13A3ADA7008ED511 /* NFSDirectory.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
6 xbmc/FileItem.cpp
@@ -759,6 +759,11 @@ bool CFileItem::IsOnDVD() const
return URIUtils::IsOnDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
}
+bool CFileItem::IsNfs() const
+{
+ return URIUtils::IsNfs(m_strPath);
+}
+
bool CFileItem::IsOnLAN() const
{
return URIUtils::IsOnLAN(m_strPath);
@@ -1999,6 +2004,7 @@ void CFileItemList::Stack()
// 1. rars and zips may be on slow sources? is this supposed to be allowed?
if( !item->IsRemote()
|| item->IsSmb()
+ || item->IsNfs()
|| URIUtils::IsInRAR(item->m_strPath)
|| URIUtils::IsInZIP(item->m_strPath)
)
View
1 xbmc/FileItem.h
@@ -116,6 +116,7 @@ class CFileItem :
bool IsOnDVD() const;
bool IsOnLAN() const;
bool IsHD() const;
+ bool IsNfs() const;
bool IsRemote() const;
bool IsSmb() const;
bool IsURL() const;
View
8 xbmc/Util.cpp
@@ -1145,8 +1145,8 @@ bool CUtil::CreateDirectoryEx(const CStdString& strPath)
// return true if directory already exist
if (CDirectory::Exists(strPath)) return true;
- // we currently only allow HD and smb paths
- if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath))
+ // we currently only allow HD and smb and nfs paths
+ if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath))
{
CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
return false;
@@ -1865,11 +1865,13 @@ bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, int iTex
bool CUtil::SupportsFileOperations(const CStdString& strPath)
{
- // currently only hd and smb support delete and rename
+ // currently only hd and smb and nfssupport delete and rename
if (URIUtils::IsHD(strPath))
return true;
if (URIUtils::IsSmb(strPath))
return true;
+ if (URIUtils::IsNfs(strPath))
+ return true;
if (URIUtils::IsMythTV(strPath))
{
/*
View
7 xbmc/filesystem/FactoryDirectory.cpp
@@ -93,6 +93,9 @@
#ifdef HAS_FILESYSTEM_SFTP
#include "SFTPDirectory.h"
#endif
+#ifdef HAS_FILESYSTEM_NFS
+#include "NFSDirectory.h"
+#endif
using namespace XFILE;
@@ -184,6 +187,10 @@ IDirectory* CFactoryDirectory::Create(const CStdString& strPath)
#ifdef HAS_ZEROCONF
if (strProtocol == "zeroconf") return new CZeroconfDirectory();
#endif
+#ifdef HAS_FILESYSTEM_NFS
+ if (strProtocol == "nfs") return new CNFSDirectory();
+#endif
+
}
CLog::Log(LOGWARNING, "%s - Unsupported protocol(%s) in %s", __FUNCTION__, strProtocol.c_str(), url.Get().c_str() );
View
8 xbmc/filesystem/FileFactory.cpp
@@ -62,6 +62,10 @@
#ifdef HAS_FILESYSTEM_SFTP
#include "FileSFTP.h"
#endif
+#ifdef HAS_FILESYSTEM_NFS
+#include "FileNFS.h"
+#endif
+
#include "FileMusicDatabase.h"
#include "FileSpecialProtocol.h"
#include "MultiPathFile.h"
@@ -154,6 +158,10 @@ IFile* CFileFactory::CreateLoader(const CURL& url)
#ifdef HAS_FILESYSTEM_VTP
else if (strProtocol == "vtp") return new CVTPFile();
#endif
+#ifdef HAS_FILESYSTEM_NFS
+ else if (strProtocol == "nfs") return new CFileNFS();
+#endif
+
}
CLog::Log(LOGWARNING, "%s - Unsupported protocol(%s) in %s", __FUNCTION__, strProtocol.c_str(), url.Get().c_str() );
View
517 xbmc/filesystem/FileNFS.cpp
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// FileNFS.cpp: implementation of the CFileNFS class.
+//
+//////////////////////////////////////////////////////////////////////
+#include "system.h"
+
+#ifdef HAS_FILESYSTEM_NFS
+#include "FileNFS.h"
+#include "NFSDirectory.h"
+#include "Util.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/GUISettings.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+#include "utils/URIUtils.h"
+
+using namespace XFILE;
+
+CStdString URLEncode(const CStdString value)
+{
+ CStdString encoded(value);
+ CURL::Encode(encoded);
+ return encoded;
+}
+
+CNfsConnection::CNfsConnection()
+: m_pNfsContext(NULL)
+, m_shareName("")
+, m_readChunkSize(0)
+, m_writeChunkSize(0)
+, m_OpenConnections(0)
+{
+}
+
+CNfsConnection::~CNfsConnection()
+{
+}
+
+void CNfsConnection::resetContext()
+{
+
+ if(m_pNfsContext)
+ {
+ m_libNfs.nfs_destroy_context(m_pNfsContext);
+ }
+
+ m_pNfsContext = m_libNfs.nfs_init_context();
+
+ if (!m_pNfsContext)
+ {
+ CLog::Log(LOGERROR,"NFS: Error initcontext in resetContext.");
+ }
+ m_writeChunkSize = 0;
+ m_readChunkSize = 0;
+ m_shareName.clear();
+ m_hostName.clear();
+}
+
+bool CNfsConnection::Connect(const CURL& url)
+{
+ CSingleLock lock(*this);
+ int ret = 0;
+ CStdString share;
+ URIUtils::GetDirectory(url.GetFileName(),share);
+ share = "/" + share;
+
+ if(!share.Equals(m_shareName,true) || !url.GetHostName().Equals(m_hostName,false) )
+ {
+ resetContext();//we need a new context because sharename or hostname has changed - old context will be freed
+
+ //we connect to the directory of the path. This will be the "root" path of this connection then.
+ //So all fileoperations are relative to this mountpoint...
+ ret = m_libNfs.nfs_mount_sync(m_pNfsContext, url.GetHostName().c_str(), share.c_str());
+
+ if (ret != 0)
+ {
+ CLog::Log(LOGERROR,"NFS: Failed to mount nfs share: %s\n", m_libNfs.nfs_get_error(m_pNfsContext));
+ return false;
+ }
+ m_shareName = share;
+ m_hostName = url.GetHostName();
+ m_readChunkSize = m_libNfs.nfs_get_readmax(m_pNfsContext);
+ m_writeChunkSize = m_libNfs.nfs_get_writemax(m_pNfsContext);
+ CLog::Log(LOGDEBUG,"NFS: Connected to server %s and export %s (chunks: r/w %i/%i)\n", url.GetHostName().c_str(), url.GetShareName().c_str(),m_readChunkSize,m_writeChunkSize);
+ }
+
+ return true;
+}
+
+/* The following two function is used to keep track on how many Opened files/directories there are.
+needed for unloading the dylib*/
+void CNfsConnection::AddActiveConnection()
+{
+ CSingleLock lock(*this);
+ if(m_OpenConnections == 0 && !m_libNfs.IsLoaded())
+ {
+ if(!m_libNfs.Load())
+ {
+ CLog::Log(LOGERROR,"NFS: Error loading libnfs (%s).",__FUNCTION__);
+ }
+ }
+ m_OpenConnections++;
+}
+
+void CNfsConnection::AddIdleConnection()
+{
+ CSingleLock lock(*this);
+ m_OpenConnections--;
+
+ if(m_OpenConnections==0)
+ {
+ if(m_pNfsContext)
+ {
+ m_libNfs.nfs_destroy_context(m_pNfsContext);
+ }
+ m_pNfsContext = NULL;
+ m_shareName.clear();
+ m_hostName.clear();
+ m_libNfs.Unload();
+ }
+}
+
+
+CNfsConnection gNfsConnection;
+
+CFileNFS::CFileNFS()
+: m_fileSize(0)
+, m_pFileHandle(NULL)
+{
+ gNfsConnection.AddActiveConnection();
+}
+
+CFileNFS::~CFileNFS()
+{
+ Close();
+ gNfsConnection.AddIdleConnection();
+}
+
+int64_t CFileNFS::GetPosition()
+{
+ int ret = 0;
+ off_t offset = 0;
+ CSingleLock lock(gNfsConnection);
+
+ if (gNfsConnection.GetNfsContext() == NULL || m_pFileHandle == NULL) return 0;
+
+ ret = (int)gNfsConnection.GetImpl()->nfs_lseek_sync(gNfsConnection.GetNfsContext(), m_pFileHandle, 0, SEEK_CUR, &offset);
+
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "NFS: Failed to lseek(%s)",gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ }
+ return offset;
+}
+
+int64_t CFileNFS::GetLength()
+{
+ if (m_pFileHandle == NULL) return 0;
+ return m_fileSize;
+}
+
+bool CFileNFS::Open(const CURL& url)
+{
+ int ret = 0;
+ Close();
+ // we can't open files like nfs://file.f or nfs://server/file.f
+ // if a file matches the if below return false, it can't exist on a nfs share.
+ if (!IsValidFile(url.GetFileName()))
+ {
+ CLog::Log(LOGNOTICE,"NFS: Bad URL : '%s'",url.GetFileName().c_str());
+ return false;
+ }
+
+ CStdString filename = "/" + URIUtils::GetFileName(url.GetFileName());
+
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ ret = gNfsConnection.GetImpl()->nfs_open_sync(gNfsConnection.GetNfsContext(), filename.c_str(), O_RDONLY, &m_pFileHandle);
+
+ if (ret != 0)
+ {
+ CLog::Log(LOGINFO, "CFileNFS::Open: Unable to open file : '%s' error : '%s'", url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG,"CFileNFS::Open - opened %s",url.GetFileName().c_str());
+ m_url=url;
+
+#ifdef _LINUX
+ struct __stat64 tmpBuffer;
+#else
+ struct stat tmpBuffer;
+#endif
+ if( Stat(&tmpBuffer) )
+ {
+ m_url.Reset();
+ Close();
+ return false;
+ }
+
+ m_fileSize = tmpBuffer.st_size;//cache the size of this file
+ // We've successfully opened the file!
+ return true;
+}
+
+
+bool CFileNFS::Exists(const CURL& url)
+{
+ CURL oldurl = m_url;
+ m_url = url;
+ bool ret = Stat(NULL) == 0;
+ m_url = oldurl;
+ return ret;
+}
+
+int CFileNFS::Stat(struct __stat64* buffer)
+{
+ return Stat(m_url,buffer);
+}
+
+
+int CFileNFS::Stat(const CURL& url, struct __stat64* buffer)
+{
+ int ret = 0;
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return -1;
+
+ CStdString filename = "/" + URIUtils::GetFileName(url.GetFileName());
+
+ struct stat tmpBuffer = {0};
+
+ ret = gNfsConnection.GetImpl()->nfs_stat_sync(gNfsConnection.GetNfsContext(), filename.c_str(), &tmpBuffer);
+
+ //if buffer == NULL we where called from Exists - in that case don't spam the log with errors
+ if (ret != 0 && buffer != NULL)
+ {
+ CLog::Log(LOGERROR, "NFS: Failed to stat(%s) %s\n", url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ ret = -1;
+ }
+ else
+ {
+ if(buffer)
+ {
+ memset(buffer, 0, sizeof(struct __stat64));
+ buffer->st_dev = tmpBuffer.st_dev;
+ buffer->st_ino = tmpBuffer.st_ino;
+ buffer->st_mode = tmpBuffer.st_mode;
+ buffer->st_nlink = tmpBuffer.st_nlink;
+ buffer->st_uid = tmpBuffer.st_uid;
+ buffer->st_gid = tmpBuffer.st_gid;
+ buffer->st_rdev = tmpBuffer.st_rdev;
+ buffer->st_size = tmpBuffer.st_size;
+ buffer->st_atime = tmpBuffer.st_atime;
+ buffer->st_mtime = tmpBuffer.st_mtime;
+ buffer->st_ctime = tmpBuffer.st_ctime;
+ }
+ }
+ return ret;
+}
+
+unsigned int CFileNFS::Read(void *lpBuf, int64_t uiBufSize)
+{
+ int numberOfBytesRead = 0;
+ int bytesLeft = uiBufSize;
+ int bytesRead = 0;
+ int chunkSize = gNfsConnection.GetMaxReadChunkSize();
+ CSingleLock lock(gNfsConnection);
+
+ if (m_pFileHandle == NULL || gNfsConnection.GetNfsContext()==NULL ) return 0;
+
+ //read chunked since nfs will only give 16kb at once
+ while(bytesLeft)
+ {
+ //last chunk could be smaller then chunk size
+ if(bytesLeft < chunkSize)
+ {
+ chunkSize = bytesLeft;
+ }
+
+ bytesRead = gNfsConnection.GetImpl()->nfs_read_sync(gNfsConnection.GetNfsContext(), m_pFileHandle, chunkSize, (char *)lpBuf+numberOfBytesRead);
+ bytesLeft -= bytesRead;
+ numberOfBytesRead += bytesRead;
+
+ if(bytesRead == 0)
+ {
+ break; //EOF
+ }
+
+ //something went wrong ...
+ if (bytesRead < 0)
+ {
+ CLog::Log(LOGERROR, "%s - Error( %d, %s )", __FUNCTION__, bytesRead, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return 0;
+ }
+
+ }
+ return (unsigned int)numberOfBytesRead;
+}
+
+int64_t CFileNFS::Seek(int64_t iFilePosition, int iWhence)
+{
+ int ret = 0;
+ off_t offset = 0;
+
+ CSingleLock lock(gNfsConnection);
+ if (m_pFileHandle == NULL || gNfsConnection.GetNfsContext()==NULL) return -1;
+
+
+ ret = (int)gNfsConnection.GetImpl()->nfs_lseek_sync(gNfsConnection.GetNfsContext(), m_pFileHandle, iFilePosition, iWhence, &offset);
+
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "%s - Error( seekpos: %"PRId64", whence: %i, fsize: %"PRId64", %s)", __FUNCTION__, iFilePosition, iWhence, m_fileSize, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return -1;
+ }
+ return (int64_t)offset;
+}
+
+void CFileNFS::Close()
+{
+ CSingleLock lock(gNfsConnection);
+
+ if (m_pFileHandle != NULL && gNfsConnection.GetNfsContext()!=NULL)
+ {
+ int ret = 0;
+ CLog::Log(LOGDEBUG,"CFileNFS::Close closing file %s", m_url.GetFileName().c_str());
+ ret = gNfsConnection.GetImpl()->nfs_close_sync(gNfsConnection.GetNfsContext(), m_pFileHandle);
+
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "Failed to close(%s) - %s\n", m_url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ }
+ m_pFileHandle=NULL;
+ m_fileSize = 0;
+ }
+}
+
+//this was a bitch!
+//for nfs write to work we have to write chunked
+//otherwise this could crash on big files
+int CFileNFS::Write(const void* lpBuf, int64_t uiBufSize)
+{
+ int numberOfBytesWritten = 0;
+ int writtenBytes = 0;
+ int leftBytes = uiBufSize;
+ int chunkSize = gNfsConnection.GetMaxWriteChunkSize();
+
+ CSingleLock lock(gNfsConnection);
+
+ if (m_pFileHandle == NULL || gNfsConnection.GetNfsContext() == NULL) return -1;
+
+ //write as long as some bytes are left to be written
+ while( leftBytes )
+ {
+ //the last chunk could be smalle than chunksize
+ if(leftBytes < chunkSize)
+ {
+ chunkSize = leftBytes;//write last chunk with correct size
+ }
+ //write chunk
+ writtenBytes = gNfsConnection.GetImpl()->nfs_write_sync(gNfsConnection.GetNfsContext(),
+ m_pFileHandle,
+ (size_t)chunkSize,
+ (char *)lpBuf + numberOfBytesWritten);
+ //decrease left bytes
+ leftBytes-= writtenBytes;
+ //increase overall written bytes
+ numberOfBytesWritten += writtenBytes;
+
+ //danger - something went wrong
+ if (writtenBytes < 0)
+ {
+ CLog::Log(LOGERROR, "Failed to pwrite(%s) %s\n", m_url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ break;
+ }
+ }
+ //return total number of written bytes
+ return numberOfBytesWritten;
+}
+
+bool CFileNFS::Delete(const CURL& url)
+{
+ int ret = 0;
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
+
+ ret = gNfsConnection.GetImpl()->nfs_unlink_sync(gNfsConnection.GetNfsContext(), filename.c_str());
+
+ if(ret != 0)
+ {
+ CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ }
+ return (ret == 0);
+}
+
+bool CFileNFS::Rename(const CURL& url, const CURL& urlnew)
+{
+ int ret = 0;
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ CStdString strFile = "//" + URIUtils::GetFileName(url.GetFileName());
+ CStdString strFileNew = "//" + URIUtils::GetFileName(urlnew.GetFileName());
+
+ ret = gNfsConnection.GetImpl()->nfs_rename_sync(gNfsConnection.GetNfsContext() , strFile.c_str(), strFileNew.c_str());
+
+ if(ret != 0)
+ {
+ CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ }
+ return (ret == 0);
+}
+
+bool CFileNFS::OpenForWrite(const CURL& url, bool bOverWrite)
+{
+ int ret = 0;
+
+ Close();
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ // we can't open files like nfs://file.f or nfs://server/file.f
+ // if a file matches the if below return false, it can't exist on a nfs share.
+ if (!IsValidFile(url.GetFileName())) return false;
+
+ CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
+
+ if (bOverWrite)
+ {
+ CLog::Log(LOGWARNING, "FileNFS::OpenForWrite() called with overwriting enabled! - %s", filename.c_str());
+ //create file with proper permissions
+ ret = gNfsConnection.GetImpl()->nfs_creat_sync(gNfsConnection.GetNfsContext(), filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &m_pFileHandle);
+ //if file was created the file handle isn't valid ... so close it and open later
+ if(ret == 0)
+ {
+ gNfsConnection.GetImpl()->nfs_close_sync(gNfsConnection.GetNfsContext(),m_pFileHandle);
+ }
+ }
+
+ ret = gNfsConnection.GetImpl()->nfs_open_sync(gNfsConnection.GetNfsContext(), filename.c_str(), O_RDWR, &m_pFileHandle);
+
+ if (ret || m_pFileHandle == NULL)
+ {
+ // write error to logfile
+ CLog::Log(LOGERROR, "CFileNFS::Open: Unable to open file : '%s' error : '%s'", filename.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return false;
+ }
+ m_url=url;
+
+#ifdef _LINUX
+ struct __stat64 tmpBuffer = {0};
+#else
+ struct stat tmpBuffer = {0};
+#endif
+ //only stat if file was not created
+ if(!bOverWrite)
+ {
+ if(Stat(&tmpBuffer))
+ {
+ m_url.Reset();
+ Close();
+ return false;
+ }
+ m_fileSize = tmpBuffer.st_size;//cache filesize of this file
+ }
+ else//file was created - filesize is zero
+ {
+ m_fileSize = 0;
+ }
+
+ // We've successfully opened the file!
+ return true;
+}
+
+bool CFileNFS::IsValidFile(const CStdString& strFileName)
+{
+ if (strFileName.Find('/') == -1 || /* doesn't have sharename */
+ strFileName.Right(2) == "/." || /* not current folder */
+ strFileName.Right(3) == "/..") /* not parent folder */
+ return false;
+ return true;
+}
+#endif//HAS_FILESYSTEM_NFS
View
75 xbmc/filesystem/FileNFS.h
@@ -0,0 +1,75 @@
+
+// FileNFS.h: interface for the CFileNFS class.
+#ifndef FILENFS_H_
+#define FILENFS_H_
+
+#include "IFile.h"
+#include "URL.h"
+#include "threads/CriticalSection.h"
+
+#include "DllLibNfs.h"
+
+CStdString URLEncode(CStdString str);
+
+class CNfsConnection : public CCriticalSection
+{
+public:
+
+ CNfsConnection();
+ ~CNfsConnection();
+ bool Connect(const CURL &url);
+ struct nfs_context *GetNfsContext(){return m_pNfsContext;}
+ size_t GetMaxReadChunkSize(){return m_readChunkSize;}
+ size_t GetMaxWriteChunkSize(){return m_writeChunkSize;}
+ DllLibNfs *GetImpl(){return &m_libNfs;}
+ void AddActiveConnection();
+ void AddIdleConnection();
+
+
+private:
+ struct nfs_context *m_pNfsContext;
+ CStdString m_shareName;
+ CStdString m_hostName;
+ size_t m_readChunkSize;
+ size_t m_writeChunkSize;
+ int m_OpenConnections;
+ DllLibNfs m_libNfs;
+ void resetContext();
+};
+
+extern CNfsConnection gNfsConnection;
+
+namespace XFILE
+{
+ class CFileNFS : public IFile
+ {
+ public:
+ CFileNFS();
+ virtual ~CFileNFS();
+ virtual void Close();
+ virtual int64_t Seek(int64_t iFilePosition, int iWhence = SEEK_SET);
+ virtual unsigned int Read(void* lpBuf, int64_t uiBufSize);
+ virtual bool Open(const CURL& url);
+ virtual bool Exists(const CURL& url);
+ virtual int Stat(const CURL& url, struct __stat64* buffer);
+ virtual int Stat(struct __stat64* buffer);
+ virtual int64_t GetLength();
+ virtual int64_t GetPosition();
+ virtual int Write(const void* lpBuf, int64_t uiBufSize);
+ //implement iocontrol for seek_possible for preventing the stat in File class for
+ //getting this info ...
+ virtual int IoControl(EIoControl request, void* param){ if(request == IOCTRL_SEEK_POSSIBLE) return 1;return -1;};
+ virtual int GetChunkSize() {return 1;}
+
+ virtual bool OpenForWrite(const CURL& url, bool bOverWrite = false);
+ virtual bool Delete(const CURL& url);
+ virtual bool Rename(const CURL& url, const CURL& urlnew);
+ protected:
+ CURL m_url;
+ bool IsValidFile(const CStdString& strFileName);
+ int64_t m_fileSize;
+ struct nfsfh *m_pFileHandle;
+ };
+}
+#endif // FILENFS_H_
+
View
217 xbmc/filesystem/NFSDirectory.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+
+#ifdef HAS_FILESYSTEM_NFS
+#include "NFSDirectory.h"
+#include "Util.h"
+#include "guilib/LocalizeStrings.h"
+#include "Application.h"
+#include "FileItem.h"
+#include "settings/AdvancedSettings.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+#include "utils/URIUtils.h"
+#include "threads/SingleLock.h"
+
+using namespace XFILE;
+using namespace std;
+
+CNFSDirectory::CNFSDirectory(void)
+{
+ gNfsConnection.AddActiveConnection();
+}
+
+CNFSDirectory::~CNFSDirectory(void)
+{
+ gNfsConnection.AddIdleConnection();
+}
+
+bool CNFSDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
+{
+ // We accept nfs://server/share/path[/file]]]]
+ int ret = 0;
+ FILETIME fileTime, localTime;
+ CSingleLock lock(gNfsConnection);
+
+ CURL url(strPath);
+
+ if(!gNfsConnection.Connect(url))
+ {
+ return false;
+ }
+
+ CStdString strDirName="//";//relative to the strPath we connected - we want to get the "/" directory then
+
+ vector<CStdString> vecEntries;
+ struct nfsdir *nfsdir = NULL;
+ struct nfsdirent *nfsdirent = NULL;
+
+ ret = gNfsConnection.GetImpl()->nfs_opendir_sync(gNfsConnection.GetNfsContext(), strDirName.c_str(), &nfsdir);
+
+ if(ret != 0)
+ {
+ CLog::Log(LOGERROR, "Failed to open(%s) %s\n", strDirName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return false;
+ }
+ lock.Leave();
+
+ while((nfsdirent = gNfsConnection.GetImpl()->nfs_readdir(gNfsConnection.GetNfsContext(), nfsdir)) != NULL)
+ {
+ vecEntries.push_back(nfsdirent->name);
+ }
+
+ lock.Enter();
+ gNfsConnection.GetImpl()->nfs_closedir(gNfsConnection.GetNfsContext(), nfsdir);//close the dir
+ lock.Leave();
+
+ for (size_t i=0; i<vecEntries.size(); i++)
+ {
+ CStdString strName = vecEntries[i];
+
+ if (!strName.Equals(".") && !strName.Equals("..")
+ && !strName.Equals("lost+found"))
+ {
+ int64_t iSize = 0;
+ bool bIsDir = false;
+ int64_t lTimeDate = 0;
+ struct stat info = {0};
+
+ CStdString strFullName = strDirName + strName;
+
+ lock.Enter();
+ ret = gNfsConnection.GetImpl()->nfs_stat_sync(gNfsConnection.GetNfsContext(), strFullName.c_str(), &info);
+ lock.Leave();
+
+ if( ret == 0 )
+ {
+ bIsDir = (info.st_mode & S_IFDIR) ? true : false;
+ lTimeDate = info.st_mtime;
+ if(lTimeDate == 0) // if modification date is missing, use create date
+ lTimeDate = info.st_ctime;
+ iSize = info.st_size;
+ }
+ else
+ CLog::Log(LOGERROR, "NFS; Failed to stat(%s) %s\n", strFullName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+
+ LONGLONG ll = Int32x32To64(lTimeDate & 0xffffffff, 10000000) + 116444736000000000ll;
+ fileTime.dwLowDateTime = (DWORD) (ll & 0xffffffff);
+ fileTime.dwHighDateTime = (DWORD)(ll >> 32);
+ FileTimeToLocalFileTime(&fileTime, &localTime);
+
+ CFileItemPtr pItem(new CFileItem(strName));
+ pItem->m_strPath = strPath + strName;
+ pItem->m_dateTime=localTime;
+
+ if (bIsDir)
+ {
+ URIUtils::AddSlashAtEnd(pItem->m_strPath);
+ pItem->m_bIsFolder = true;
+ }
+ else
+ {
+ pItem->m_bIsFolder = false;
+ pItem->m_dwSize = iSize;
+ }
+ items.Add(pItem);
+ }
+ }
+ return true;
+}
+
+bool CNFSDirectory::Create(const char* strPath)
+{
+ int ret = 0;
+ int newFolderLen = 0;
+
+ CSingleLock lock(gNfsConnection);
+
+ CURL url(URIUtils::GetParentPath(strPath));
+ CStdString folderName(strPath);
+ newFolderLen = folderName.length() - URIUtils::GetParentPath(strPath).length();
+ folderName = "//" + folderName.Right(newFolderLen);
+
+ URIUtils::RemoveSlashAtEnd(folderName);//mkdir fails if a slash is at the end!!!
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ ret = gNfsConnection.GetImpl()->nfs_mkdir_sync(gNfsConnection.GetNfsContext(), folderName.c_str());
+
+ if(ret != 0)
+ CLog::Log(LOGERROR, "NFS: Failed to create(%s) %s\n", folderName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return (ret == 0 || EEXIST == ret);
+}
+
+bool CNFSDirectory::Remove(const char* strPath)
+{
+ int ret = 0;
+ int delFolderLen = 0;
+
+ CSingleLock lock(gNfsConnection);
+
+ CURL url(URIUtils::GetParentPath(strPath));
+ CStdString folderName(strPath);
+ delFolderLen = folderName.length() - URIUtils::GetParentPath(strPath).length();
+ folderName = "//" + folderName.Right(delFolderLen);
+
+ URIUtils::RemoveSlashAtEnd(folderName);//rmdir fails if a slash is at the end!!!
+
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ ret = gNfsConnection.GetImpl()->nfs_rmdir_sync(gNfsConnection.GetNfsContext(), folderName.c_str());
+
+ if(ret != 0 && errno != ENOENT)
+ {
+ CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return false;
+ }
+ return true;
+}
+
+bool CNFSDirectory::Exists(const char* strPath)
+{
+ int ret = 0;
+ int existFolderLen = 0;
+ CSingleLock lock(gNfsConnection);
+
+ CURL url(URIUtils::GetParentPath(strPath));
+ CStdString folderName(strPath);
+ existFolderLen = folderName.length() - URIUtils::GetParentPath(strPath).length();
+ folderName = "//" + folderName.Right(existFolderLen);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ struct stat info;
+ ret = gNfsConnection.GetImpl()->nfs_stat_sync(gNfsConnection.GetNfsContext(), folderName.c_str(), &info);
+
+ if (ret != 0)
+ {
+ return false;
+ }
+ return (info.st_mode & S_IFDIR) ? true : false;
+}
+
+#endif
View
41 xbmc/filesystem/NFSDirectory.h
@@ -0,0 +1,41 @@
+#pragma once
+/*
+ * Copyright (C) 2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "IDirectory.h"
+#include "FileNFS.h"
+#include "MediaSource.h"
+
+namespace XFILE
+{
+ class CNFSDirectory : public IDirectory
+ {
+ public:
+ CNFSDirectory(void);
+ virtual ~CNFSDirectory(void);
+ virtual bool GetDirectory(const CStdString& strPath, CFileItemList &items);
+ virtual DIR_CACHE_TYPE GetCacheType(const CStdString &strPath) const { return DIR_CACHE_ONCE; };
+ virtual bool Create(const char* strPath);
+ virtual bool Exists(const char* strPath);
+ virtual bool Remove(const char* strPath);
+ };
+}
+
View
5 xbmc/system.h
@@ -64,6 +64,11 @@
#define HAS_FILESYSTEM_VTP
#define HAS_FILESYSTEM_HTSP
+#ifdef HAVE_LIBNFS
+ #define HAS_FILESYSTEM_NFS
+#endif
+
+
/**********************
* Non-free Components
**********************/
View
11 xbmc/utils/URIUtils.cpp
@@ -683,6 +683,17 @@ bool URIUtils::IsMusicDb(const CStdString& strFile)
return strFile.Left(8).Equals("musicdb:");
}
+bool URIUtils::IsNfs(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return strFile2.Left(4).Equals("nfs:");
+}
+
+
bool URIUtils::IsVideoDb(const CStdString& strFile)
{
return strFile.Left(8).Equals("videodb:");
View
1 xbmc/utils/URIUtils.h
@@ -68,6 +68,7 @@ class URIUtils
static bool IsMultiPath(const CStdString& strPath);
static bool IsMusicDb(const CStdString& strFile);
static bool IsMythTV(const CStdString& strFile);
+ static bool IsNfs(const CStdString& strFile);
static bool IsOnDVD(const CStdString& strFile);
static bool IsOnLAN(const CStdString& strFile);
static bool IsPlugin(const CStdString& strFile);

0 comments on commit 9ec9cab

Please sign in to comment.
Something went wrong with that request. Please try again.